Skip to content

Commit 7266200

Browse files
committed
fix(parser): parse @x() @y() export default abstract class {} (#11630)
Also took the opportunity to reduce a rewind on `export default interface`.
1 parent 0bdf718 commit 7266200

File tree

4 files changed

+129
-69
lines changed

4 files changed

+129
-69
lines changed

crates/oxc_codegen/tests/integration/ts.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ fn cases() {
1616
);
1717
test_same("class B {\n\tconstructor(override readonly a: number) {}\n}\n");
1818
test_same("export { type as as };\n");
19-
test_same("@Decorator() abstract class C {}\n");
19+
}
20+
21+
#[test]
22+
fn decorators() {
23+
test_same("@a abstract class C {}\n");
24+
test_tsx("@a @b export default abstract class {}", "export default @a @b abstract class {}\n");
2025
}
2126

2227
#[test]

crates/oxc_parser/src/js/module.rs

Lines changed: 116 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ use oxc_span::GetSpan;
44
use rustc_hash::FxHashMap;
55

66
use super::FunctionKind;
7-
use crate::{Context, ParserImpl, diagnostics, lexer::Kind, modifiers::Modifiers};
7+
use crate::{
8+
Context, ParserImpl, diagnostics,
9+
lexer::Kind,
10+
modifiers::{Modifier, ModifierFlags, ModifierKind, Modifiers},
11+
};
812

913
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1014
enum ImportOrExport {
@@ -548,78 +552,127 @@ impl<'a> ParserImpl<'a> {
548552
decorators: Vec<'a, Decorator<'a>>,
549553
) -> Box<'a, ExportDefaultDeclaration<'a>> {
550554
let exported = self.parse_keyword_identifier(Kind::Default);
555+
let declaration = self.parse_export_default_declaration_kind(decorators);
556+
let exported = ModuleExportName::IdentifierName(exported);
557+
let span = self.end_span(span);
558+
self.ast.alloc_export_default_declaration(span, exported, declaration)
559+
}
560+
561+
fn parse_export_default_declaration_kind(
562+
&mut self,
563+
mut decorators: Vec<'a, Decorator<'a>>,
564+
) -> ExportDefaultDeclarationKind<'a> {
551565
let decl_span = self.start_span();
566+
567+
// export default /* @__NO_SIDE_EFFECTS__ */ ...
552568
let has_no_side_effects_comment =
553569
self.lexer.trivia_builder.previous_token_has_no_side_effects_comment();
554-
let kind = self.cur_kind();
555-
if kind != Kind::Class {
556-
for decorator in &decorators {
557-
self.error(diagnostics::decorators_are_not_valid_here(decorator.span));
570+
571+
// export default @decorator ...
572+
if self.at(Kind::At) {
573+
let after_export_decorators = self.parse_decorators();
574+
// @decorator export default @decorator ...
575+
if !decorators.is_empty() {
576+
for decorator in &after_export_decorators {
577+
self.error(diagnostics::decorators_in_export_and_class(decorator.span));
578+
}
558579
}
580+
decorators.extend(after_export_decorators);
559581
}
560-
let declaration = match kind {
561-
Kind::Class => ExportDefaultDeclarationKind::ClassDeclaration(
562-
self.parse_class_declaration(decl_span, &Modifiers::empty(), decorators),
563-
),
564-
Kind::At => {
565-
let decorators = self.parse_decorators();
566-
let modifiers = self.parse_modifiers(false, false);
567-
ExportDefaultDeclarationKind::ClassDeclaration(
568-
self.parse_class_declaration(decl_span, &modifiers, decorators),
569-
)
570-
}
571-
_ if self.is_ts
572-
&& self.at(Kind::Abstract)
573-
&& self.lookahead(|p| {
574-
p.bump_any();
575-
p.at(Kind::Class)
576-
}) =>
577-
{
578-
// `export default abstract class ...`
579-
// eat the abstract modifier
580-
let modifiers = self.eat_modifiers_before_declaration();
581-
ExportDefaultDeclarationKind::ClassDeclaration(self.parse_class_declaration(
582-
decl_span,
583-
&modifiers,
584-
self.ast.vec(),
585-
))
586-
}
587-
_ if self.is_ts
588-
&& self.at(Kind::Interface)
589-
&& self.lookahead(|p| {
590-
p.bump_any();
591-
!p.cur_token().is_on_new_line()
592-
}) =>
593-
{
594-
// `export default interface [no line break here] ...`
595-
let decl = self.parse_ts_interface_declaration(decl_span, &Modifiers::empty());
596-
match decl {
597-
Declaration::TSInterfaceDeclaration(decl) => {
598-
ExportDefaultDeclarationKind::TSInterfaceDeclaration(decl)
582+
583+
let function_span = self.start_span();
584+
585+
let checkpoint = self.checkpoint();
586+
let mut is_abstract = false;
587+
let mut is_async = false;
588+
let mut is_interface = false;
589+
590+
match self.cur_kind() {
591+
Kind::Abstract => is_abstract = true,
592+
Kind::Async => is_async = true,
593+
Kind::Interface => is_interface = true,
594+
_ => {}
595+
}
596+
597+
if is_abstract || is_async || is_interface {
598+
let modifier_span = self.start_span();
599+
self.bump_any();
600+
let cur_token = self.cur_token();
601+
let kind = cur_token.kind();
602+
if !cur_token.is_on_new_line() {
603+
// export default abstract class ...
604+
if is_abstract && kind == Kind::Class {
605+
let modifiers = self
606+
.ast
607+
.vec1(Modifier::new(self.end_span(modifier_span), ModifierKind::Abstract));
608+
let modifiers = Modifiers::new(Some(modifiers), ModifierFlags::ABSTRACT);
609+
return ExportDefaultDeclarationKind::ClassDeclaration(
610+
self.parse_class_declaration(decl_span, &modifiers, decorators),
611+
);
612+
}
613+
614+
// export default async function ...
615+
if is_async && kind == Kind::Function {
616+
for decorator in &decorators {
617+
self.error(diagnostics::decorators_are_not_valid_here(decorator.span));
599618
}
600-
_ => unreachable!(),
619+
let mut func = self.parse_function_impl(
620+
function_span,
621+
/* r#async */ true,
622+
FunctionKind::DefaultExport,
623+
);
624+
if has_no_side_effects_comment {
625+
func.pure = true;
626+
}
627+
return ExportDefaultDeclarationKind::FunctionDeclaration(func);
601628
}
602-
}
603-
_ if self.at_function_with_async() => {
604-
let span = self.start_span();
605-
let r#async = self.eat(Kind::Async);
606-
let mut func = self.parse_function_impl(span, r#async, FunctionKind::DefaultExport);
607-
if has_no_side_effects_comment {
608-
func.pure = true;
629+
630+
// export default interface ...
631+
if is_interface {
632+
for decorator in &decorators {
633+
self.error(diagnostics::decorators_are_not_valid_here(decorator.span));
634+
}
635+
if let Declaration::TSInterfaceDeclaration(decl) =
636+
self.parse_ts_interface_declaration(modifier_span, &Modifiers::empty())
637+
{
638+
return ExportDefaultDeclarationKind::TSInterfaceDeclaration(decl);
639+
}
609640
}
610-
ExportDefaultDeclarationKind::FunctionDeclaration(func)
611641
}
612-
_ => {
613-
let decl = ExportDefaultDeclarationKind::from(
614-
self.parse_assignment_expression_or_higher(),
615-
);
616-
self.asi();
617-
decl
642+
self.rewind(checkpoint);
643+
}
644+
645+
let kind = self.cur_kind();
646+
// export default class ...
647+
if kind == Kind::Class {
648+
return ExportDefaultDeclarationKind::ClassDeclaration(self.parse_class_declaration(
649+
decl_span,
650+
&Modifiers::empty(),
651+
decorators,
652+
));
653+
}
654+
655+
for decorator in &decorators {
656+
self.error(diagnostics::decorators_are_not_valid_here(decorator.span));
657+
}
658+
659+
// export default function ...
660+
if kind == Kind::Function {
661+
let mut func = self.parse_function_impl(
662+
function_span,
663+
/* r#async */ false,
664+
FunctionKind::DefaultExport,
665+
);
666+
if has_no_side_effects_comment {
667+
func.pure = true;
618668
}
619-
};
620-
let exported = ModuleExportName::IdentifierName(exported);
621-
let span = self.end_span(span);
622-
self.ast.alloc_export_default_declaration(span, exported, declaration)
669+
return ExportDefaultDeclarationKind::FunctionDeclaration(func);
670+
}
671+
672+
// export default expr
673+
let decl = ExportDefaultDeclarationKind::from(self.parse_assignment_expression_or_higher());
674+
self.asi();
675+
decl
623676
}
624677

625678
// export ExportFromClause FromClause ;

crates/oxc_parser/src/ts/statement.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ impl<'a> ParserImpl<'a> {
181181
span: u32,
182182
modifiers: &Modifiers<'a>,
183183
) -> Declaration<'a> {
184-
self.expect(Kind::Interface); // bump interface
185184
let id = self.parse_binding_identifier();
186185
let type_parameters = self.parse_ts_type_parameters();
187186
let (extends, implements) = self.parse_heritage_clause();
@@ -430,7 +429,10 @@ impl<'a> ParserImpl<'a> {
430429
}
431430
Kind::Type => self.parse_ts_type_alias_declaration(start_span, modifiers),
432431
Kind::Enum => self.parse_ts_enum_declaration(start_span, modifiers),
433-
Kind::Interface => self.parse_ts_interface_declaration(start_span, modifiers),
432+
Kind::Interface => {
433+
self.bump_any();
434+
self.parse_ts_interface_declaration(start_span, modifiers)
435+
}
434436
Kind::Class => {
435437
let decl = self.parse_class_declaration(start_span, modifiers, decorators);
436438
Declaration::ClassDeclaration(decl)

tasks/coverage/snapshots/parser_misc.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ Negative Passed: 45/45 (100.00%)
4242
2
4343
╰────
4444

45-
× Decorators are not valid here.
46-
╭─[misc/fail/oxc-11472.js:3:1]
45+
× Decorators may not appear after 'export' or 'export default' if they also appear before 'export'.
46+
╭─[misc/fail/oxc-11472.js:3:22]
4747
2
4848
3 │ @dec1 export default @dec2 class {}
49-
· ─────
49+
· ─────
5050
╰────
5151

5252
× Expected `from` but found `EOF`

0 commit comments

Comments
 (0)