Skip to content

Commit b7b0dc3

Browse files
committed
refactor(parser): improve TSModuleDeclaration parsing (#11605)
closes #11592
1 parent e9a8832 commit b7b0dc3

File tree

9 files changed

+107
-102
lines changed

9 files changed

+107
-102
lines changed

crates/oxc_linter/src/rules/typescript/prefer_namespace_keyword.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ fn test() {
8787
"namespace foo {}",
8888
"declare namespace foo {}",
8989
"declare global {}",
90-
"module''.s",
9190
];
9291

9392
let fail = vec![
@@ -137,7 +136,6 @@ fn test() {
137136
",
138137
None,
139138
),
140-
("module foo.'a'", "namespace foo.'a'", None),
141139
];
142140

143141
Tester::new(PreferNamespaceKeyword::NAME, PreferNamespaceKeyword::PLUGIN, pass, fail)

crates/oxc_linter/src/snapshots/typescript_prefer_namespace_keyword.snap

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,8 @@ source: crates/oxc_linter/src/tester.rs
5050
╰────
5151
help: Replace `module` with `namespace`.
5252

53-
typescript-eslint(prefer-namespace-keyword): Use 'namespace' instead of 'module' to declare custom TypeScript modules.
54-
╭─[prefer_namespace_keyword.tsx:1:1]
53+
× Unexpected token
54+
╭─[prefer_namespace_keyword.tsx:1:12]
5555
1module foo.'a'
56-
· ──────────────
56+
· ───
5757
╰────
58-
help: Replace `module` with `namespace`.

crates/oxc_parser/src/modifiers.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ bitflags! {
2626
const CONST = 1 << 9;
2727
const IN = 1 << 10;
2828
const OUT = 1 << 11;
29-
const EXPORT = 1 << 12;
3029
const DEFAULT = 1 << 13;
3130
const ACCESSOR = 1 << 14;
3231
const ACCESSIBILITY = Self::PRIVATE.bits() | Self::PROTECTED.bits() | Self::PUBLIC.bits();

crates/oxc_parser/src/ts/statement.rs

Lines changed: 79 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1+
use bitflags::bitflags;
12
use oxc_allocator::{Box, Vec};
23
use oxc_ast::ast::*;
34
use oxc_span::GetSpan;
45

6+
bitflags! {
7+
#[derive(Debug, Clone, Copy)]
8+
pub struct ParseModuleDeclarationFlags: u8 {
9+
const Namespace = 1 << 0;
10+
const NestedNamespace = 1 << 1;
11+
}
12+
}
13+
514
use crate::{
615
ParserImpl, diagnostics,
716
js::{FunctionKind, VariableDeclarationParent},
@@ -260,6 +269,56 @@ impl<'a> ParserImpl<'a> {
260269

261270
/* ----------------------- Namespace & Module ----------------------- */
262271

272+
fn parse_ts_module_declaration(
273+
&mut self,
274+
span: u32,
275+
modifiers: &Modifiers<'a>,
276+
) -> Box<'a, TSModuleDeclaration<'a>> {
277+
let mut flags = ParseModuleDeclarationFlags::empty();
278+
let kind;
279+
if self.at(Kind::Global) {
280+
kind = TSModuleDeclarationKind::Global;
281+
return self.parse_ambient_external_module_declaration(span, kind, modifiers);
282+
} else if self.eat(Kind::Namespace) {
283+
kind = TSModuleDeclarationKind::Namespace;
284+
flags.insert(ParseModuleDeclarationFlags::Namespace);
285+
} else {
286+
self.expect(Kind::Module);
287+
kind = TSModuleDeclarationKind::Module;
288+
if self.at(Kind::Str) {
289+
return self.parse_ambient_external_module_declaration(span, kind, modifiers);
290+
}
291+
}
292+
self.parse_module_or_namespace_declaration(span, kind, modifiers, flags)
293+
}
294+
295+
fn parse_ambient_external_module_declaration(
296+
&mut self,
297+
span: u32,
298+
kind: TSModuleDeclarationKind,
299+
modifiers: &Modifiers<'a>,
300+
) -> Box<'a, TSModuleDeclaration<'a>> {
301+
let id = if self.at(Kind::Global) {
302+
TSModuleDeclarationName::Identifier(self.parse_binding_identifier())
303+
} else {
304+
TSModuleDeclarationName::StringLiteral(self.parse_literal_string())
305+
};
306+
let body = if self.at(Kind::LCurly) {
307+
let block = self.parse_ts_module_block();
308+
Some(TSModuleDeclarationBody::TSModuleBlock(block))
309+
} else {
310+
self.asi();
311+
None
312+
};
313+
self.ast.alloc_ts_module_declaration(
314+
self.end_span(span),
315+
id,
316+
body,
317+
kind,
318+
modifiers.contains_declare(),
319+
)
320+
}
321+
263322
fn parse_ts_module_block(&mut self) -> Box<'a, TSModuleBlock<'a>> {
264323
let span = self.start_span();
265324
self.expect(Kind::LCurly);
@@ -269,45 +328,41 @@ impl<'a> ParserImpl<'a> {
269328
self.ast.alloc_ts_module_block(self.end_span(span), directives, statements)
270329
}
271330

272-
pub(crate) fn parse_ts_namespace_or_module_declaration_body(
331+
fn parse_module_or_namespace_declaration(
273332
&mut self,
274333
span: u32,
275334
kind: TSModuleDeclarationKind,
276335
modifiers: &Modifiers<'a>,
336+
flags: ParseModuleDeclarationFlags,
277337
) -> Box<'a, TSModuleDeclaration<'a>> {
278-
self.verify_modifiers(
279-
modifiers,
280-
ModifierFlags::DECLARE | ModifierFlags::EXPORT,
281-
diagnostics::modifier_cannot_be_used_here,
282-
);
283-
let id = match self.cur_kind() {
284-
Kind::Str => TSModuleDeclarationName::StringLiteral(self.parse_literal_string()),
285-
_ => TSModuleDeclarationName::Identifier(self.parse_binding_identifier()),
286-
};
287-
338+
let id = // if flags.intersects(ParseModuleDeclarationFlags::NestedNamespace) {
339+
// TODO: missing identifier name in AST.
340+
// TSModuleDeclarationName::IdentifierName(self.parse_identifier_name());
341+
// } else {
342+
TSModuleDeclarationName::Identifier(self.parse_binding_identifier());
343+
// };
288344
let body = if self.eat(Kind::Dot) {
289345
let span = self.start_span();
290-
let decl =
291-
self.parse_ts_namespace_or_module_declaration_body(span, kind, &Modifiers::empty());
292-
Some(TSModuleDeclarationBody::TSModuleDeclaration(decl))
293-
} else if self.at(Kind::LCurly) {
294-
let block = self.parse_ts_module_block();
295-
Some(TSModuleDeclarationBody::TSModuleBlock(block))
346+
let decl = self.parse_module_or_namespace_declaration(
347+
span,
348+
kind,
349+
&Modifiers::empty(),
350+
flags.union(ParseModuleDeclarationFlags::NestedNamespace),
351+
);
352+
TSModuleDeclarationBody::TSModuleDeclaration(decl)
296353
} else {
297-
self.asi();
298-
None
354+
let block = self.parse_ts_module_block();
355+
TSModuleDeclarationBody::TSModuleBlock(block)
299356
};
300-
301357
self.verify_modifiers(
302358
modifiers,
303359
ModifierFlags::DECLARE,
304360
diagnostics::modifier_cannot_be_used_here,
305361
);
306-
307362
self.ast.alloc_ts_module_declaration(
308363
self.end_span(span),
309364
id,
310-
body,
365+
Some(body),
311366
kind,
312367
modifiers.contains_declare(),
313368
)
@@ -340,25 +395,8 @@ impl<'a> ParserImpl<'a> {
340395
}
341396
}
342397
match kind {
343-
Kind::Namespace => {
344-
let kind = TSModuleDeclarationKind::Namespace;
345-
self.bump_any();
346-
let decl =
347-
self.parse_ts_namespace_or_module_declaration_body(start_span, kind, modifiers);
348-
Declaration::TSModuleDeclaration(decl)
349-
}
350-
Kind::Module => {
351-
let kind = TSModuleDeclarationKind::Module;
352-
self.bump_any();
353-
let decl =
354-
self.parse_ts_namespace_or_module_declaration_body(start_span, kind, modifiers);
355-
Declaration::TSModuleDeclaration(decl)
356-
}
357-
Kind::Global => {
358-
// declare global { }
359-
let kind = TSModuleDeclarationKind::Global;
360-
let decl =
361-
self.parse_ts_namespace_or_module_declaration_body(start_span, kind, modifiers);
398+
Kind::Global | Kind::Module | Kind::Namespace => {
399+
let decl = self.parse_ts_module_declaration(start_span, modifiers);
362400
Declaration::TSModuleDeclaration(decl)
363401
}
364402
Kind::Type => self.parse_ts_type_alias_declaration(start_span, modifiers),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
namespace "a" {}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
namespace "a";
2+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
namespace a;

tasks/coverage/snapshots/parser_misc.snap

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
parser_misc Summary:
22
AST Parsed : 41/41 (100.00%)
33
Positive Passed: 41/41 (100.00%)
4-
Negative Passed: 40/40 (100.00%)
4+
Negative Passed: 43/43 (100.00%)
55

66
× Identifier `b` has already been declared
77
╭─[misc/fail/oxc-10159.js:1:22]
@@ -75,6 +75,26 @@ Negative Passed: 40/40 (100.00%)
7575
· ╰── `}` expected
7676
╰────
7777

78+
× Unexpected token
79+
╭─[misc/fail/oxc-11592-1.ts:1:11]
80+
1namespace "a" {}
81+
· ───
82+
╰────
83+
84+
× Unexpected token
85+
╭─[misc/fail/oxc-11592-2.ts:1:11]
86+
1namespace "a";
87+
· ───
88+
2
89+
╰────
90+
91+
× Expected `{` but found `;`
92+
╭─[misc/fail/oxc-11592-3.ts:1:12]
93+
1 │ namespace a;
94+
· ┬
95+
· ╰── `{` expected
96+
╰────
97+
7898
× Unexpected token
7999
╭─[misc/fail/oxc-169.js:2:1]
80100
11<(V=82<<t-j0<(V=$<LBI<(V=ut<I<(V=$<LBI<(V=uIV=82<<t-j0<(V=$<LBI<(V=ut<I<(V=$<LBI<(V<II>

tasks/coverage/snapshots/parser_typescript.snap

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -18579,13 +18579,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
1857918579
2 │ }
1858018580
╰────
1858118581

18582-
× 'async' modifier cannot be used here.
18583-
╭─[typescript/tests/cases/conformance/async/es5/asyncModule_es5.ts:1:1]
18584-
1 │ async module M {
18585-
· ─────
18586-
2 │ }
18587-
╰────
18588-
1858918582
× 'async' modifier cannot be used here.
1859018583
╭─[typescript/tests/cases/conformance/async/es5/asyncSetter_es5.ts:2:3]
1859118584
1 │ class C {
@@ -18720,13 +18713,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
1872018713
2 │ }
1872118714
╰────
1872218715

18723-
× 'async' modifier cannot be used here.
18724-
╭─[typescript/tests/cases/conformance/async/es6/asyncModule_es6.ts:1:1]
18725-
1 │ async module M {
18726-
· ─────
18727-
2 │ }
18728-
╰────
18729-
1873018716
× 'async' modifier cannot be used here.
1873118717
╭─[typescript/tests/cases/conformance/async/es6/asyncSetter_es6.ts:2:3]
1873218718
1 │ class C {
@@ -20614,14 +20600,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2061420600
× 'accessor' modifier cannot be used here.
2061520601
╭─[typescript/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessorDisallowedModifiers.ts:29:1]
2061620602
28 │ accessor interface I2 {}
20617-
29 │ accessor namespace N1 {}
20618-
· ────────
20619-
30 │ accessor enum E1 {}
20620-
╰────
20621-
20622-
× 'accessor' modifier cannot be used here.
20623-
╭─[typescript/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessorDisallowedModifiers.ts:29:1]
20624-
28 │ accessor interface I2 {}
2062520603
29 │ accessor namespace N1 {}
2062620604
· ────────
2062720605
30 │ accessor enum E1 {}
@@ -25550,14 +25528,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2555025528
× 'public' modifier cannot be used here.
2555125529
╭─[typescript/tests/cases/conformance/internalModules/moduleBody/invalidModuleWithStatementsOfEveryKind.ts:19:5]
2555225530
18 │ module Y3 {
25553-
19 │ public module Module {
25554-
· ──────
25555-
20 │ class A { s: string }
25556-
╰────
25557-
25558-
× 'public' modifier cannot be used here.
25559-
╭─[typescript/tests/cases/conformance/internalModules/moduleBody/invalidModuleWithStatementsOfEveryKind.ts:19:5]
25560-
18 │ module Y3 {
2556125531
19 │ public module Module {
2556225532
· ──────
2556325533
20 │ class A { s: string }
@@ -25614,14 +25584,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2561425584
× 'private' modifier cannot be used here.
2561525585
╭─[typescript/tests/cases/conformance/internalModules/moduleBody/invalidModuleWithStatementsOfEveryKind.ts:44:5]
2561625586
43 │ module YY3 {
25617-
44 │ private module Module {
25618-
· ───────
25619-
45 │ class A { s: string }
25620-
╰────
25621-
25622-
× 'private' modifier cannot be used here.
25623-
╭─[typescript/tests/cases/conformance/internalModules/moduleBody/invalidModuleWithStatementsOfEveryKind.ts:44:5]
25624-
43 │ module YY3 {
2562525587
44 │ private module Module {
2562625588
· ───────
2562725589
45 │ class A { s: string }
@@ -25678,14 +25640,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2567825640
× 'static' modifier cannot be used here.
2567925641
╭─[typescript/tests/cases/conformance/internalModules/moduleBody/invalidModuleWithStatementsOfEveryKind.ts:70:5]
2568025642
69 │ module YYY3 {
25681-
70 │ static module Module {
25682-
· ──────
25683-
71 │ class A { s: string }
25684-
╰────
25685-
25686-
× 'static' modifier cannot be used here.
25687-
╭─[typescript/tests/cases/conformance/internalModules/moduleBody/invalidModuleWithStatementsOfEveryKind.ts:70:5]
25688-
69 │ module YYY3 {
2568925643
70 │ static module Module {
2569025644
· ──────
2569125645
71 │ class A { s: string }
@@ -27896,13 +27850,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2789627850
2 │ }
2789727851
╰────
2789827852

27899-
× 'protected' modifier cannot be used here.
27900-
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Protected/Protected2.ts:1:1]
27901-
1 │ protected module M {
27902-
· ─────────
27903-
2 │ }
27904-
╰────
27905-
2790627853
× Expected a semicolon or an implicit semicolon after a statement, but found none
2790727854
╭─[typescript/tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts:1430:16]
2790827855
1429 │ // Regex for parsing options in the format "@Alpha: Value of any sort"

0 commit comments

Comments
 (0)