Skip to content

Commit cbb27fd

Browse files
committed
fix(ast)!: add TSGlobalDeclaration type (#15712)
Fix #14860. ### The problem `TSModuleDeclaration` type was used to represent 4 different syntaxes: * `namespace X {}` * `module X {}` * `module "x" {}` * `global {}` (and all the above prefixed with `declare`) `global {}` is quite different from the others in a few ways: * It has no identifier (`global` is a keyword, not an identifier). * It has no symbol. * The `body` block is not optional. * Directives are not allowed in the `body` block. * It is always ambient context (regardless of whether it has `declare` modifier). In particular, it was causing panics because `symbol_id` field for `TSModuleDeclaration`s representing `global {}` was `None`. ### Solution Rather than trying to make `TSModuleDeclaration` type encode all these differences, and make Oxc's code reason about them, split `global {}` into a new type `TSGlobalDeclaration`. The rest of the diff is just adjusting code to also handle `TSGlobalDeclaration` where it was dealing with `TSModuleDeclaration`s. This change simplifies the parser quite a bit. `oxc_semantic`'s `SymbolDeclarationTest` tester now tests that all `TSModuleDeclaration`s with `id: BindingIdentifier` do have a symbol: https://github.com/oxc-project/oxc/pull/15712/files#diff-ce544d71b839faa898887ac920237e563e59a708e7fca86b53023498ab67a611L101-R110 ### Formatter bug There is one regression in the formatter for `typescript/module/global.ts` case. https://github.com/oxc-project/oxc/pull/15712/files#diff-f248b1d49b8c482864810055632d7322cdebc895dd4dfe05fbefcf5e23ba12c9R51 Input: ```ts declare /* module */ global {} declare /* namespace */ global {} ``` Output: ```diff - declare /* module */ global {} - declare /* namespace */ global {} + declare global /* module */ {} + declare global /* namespace */ {} ``` I assume this is happening because there's no longer a `BindingIdentifier` for the `global` keyword for the comment to get attached to. I am not sure how to fix this. Maybe the `global_span` field can help? @Dunqing This PR touches a *lot* of files. Can I propose that we merge it before it gets conflicts, and if you can give me some guidance, I'll do a follow-up PR to fix it?
1 parent 9b7b083 commit cbb27fd

File tree

76 files changed

+1685
-614
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1685
-614
lines changed

apps/oxlint/src-js/generated/deserialize.js

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,8 @@ function deserializeStatement(pos) {
14581458
case 38:
14591459
return deserializeBoxTSModuleDeclaration(pos + 8);
14601460
case 39:
1461+
return deserializeBoxTSGlobalDeclaration(pos + 8);
1462+
case 40:
14611463
return deserializeBoxTSImportEqualsDeclaration(pos + 8);
14621464
case 64:
14631465
return deserializeBoxImportDeclaration(pos + 8);
@@ -1544,6 +1546,8 @@ function deserializeDeclaration(pos) {
15441546
case 38:
15451547
return deserializeBoxTSModuleDeclaration(pos + 8);
15461548
case 39:
1549+
return deserializeBoxTSGlobalDeclaration(pos + 8);
1550+
case 40:
15471551
return deserializeBoxTSImportEqualsDeclaration(pos + 8);
15481552
default:
15491553
throw Error(`Unexpected discriminant ${uint8[pos]} for Declaration`);
@@ -4933,7 +4937,6 @@ function deserializeTSTypePredicateName(pos) {
49334937

49344938
function deserializeTSModuleDeclaration(pos) {
49354939
let kind = deserializeTSModuleDeclarationKind(pos + 84),
4936-
global = kind === 'global',
49374940
start = deserializeU32(pos),
49384941
end = deserializeU32(pos + 4),
49394942
declare = deserializeBool(pos + 85),
@@ -4947,7 +4950,7 @@ function deserializeTSModuleDeclaration(pos) {
49474950
id: null,
49484951
kind,
49494952
declare,
4950-
global,
4953+
global: false,
49514954
start,
49524955
end,
49534956
range: [start, end],
@@ -4962,7 +4965,7 @@ function deserializeTSModuleDeclaration(pos) {
49624965
body,
49634966
kind,
49644967
declare,
4965-
global,
4968+
global: false,
49664969
start,
49674970
end,
49684971
range: [start, end],
@@ -5031,10 +5034,8 @@ function deserializeTSModuleDeclaration(pos) {
50315034
function deserializeTSModuleDeclarationKind(pos) {
50325035
switch (uint8[pos]) {
50335036
case 0:
5034-
return 'global';
5035-
case 1:
50365037
return 'module';
5037-
case 2:
5038+
case 1:
50385039
return 'namespace';
50395040
default:
50405041
throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleDeclarationKind`);
@@ -5063,6 +5064,44 @@ function deserializeTSModuleDeclarationBody(pos) {
50635064
}
50645065
}
50655066

5067+
function deserializeTSGlobalDeclaration(pos) {
5068+
let start = deserializeU32(pos),
5069+
end = deserializeU32(pos + 4),
5070+
previousParent = parent,
5071+
node = (parent = {
5072+
__proto__: NodeProto,
5073+
type: 'TSModuleDeclaration',
5074+
id: null,
5075+
body: null,
5076+
kind: null,
5077+
declare: deserializeBool(pos + 76),
5078+
global: null,
5079+
start,
5080+
end,
5081+
range: [start, end],
5082+
parent,
5083+
}),
5084+
keywordStart,
5085+
keywordEnd;
5086+
node.id = {
5087+
__proto__: NodeProto,
5088+
type: 'Identifier',
5089+
decorators: [],
5090+
name: 'global',
5091+
optional: false,
5092+
typeAnnotation: null,
5093+
start: (keywordStart = deserializeU32(pos + 8)),
5094+
end: (keywordEnd = deserializeU32(pos + 12)),
5095+
range: [keywordStart, keywordEnd],
5096+
parent,
5097+
};
5098+
node.body = deserializeTSModuleBlock(pos + 16);
5099+
node.kind = 'global';
5100+
node.global = true;
5101+
parent = previousParent;
5102+
return node;
5103+
}
5104+
50665105
function deserializeTSModuleBlock(pos) {
50675106
let start = deserializeU32(pos),
50685107
end = deserializeU32(pos + 4),
@@ -6257,6 +6296,10 @@ function deserializeBoxTSModuleDeclaration(pos) {
62576296
return deserializeTSModuleDeclaration(uint32[pos >> 2]);
62586297
}
62596298

6299+
function deserializeBoxTSGlobalDeclaration(pos) {
6300+
return deserializeTSGlobalDeclaration(uint32[pos >> 2]);
6301+
}
6302+
62606303
function deserializeBoxTSImportEqualsDeclaration(pos) {
62616304
return deserializeTSImportEqualsDeclaration(uint32[pos >> 2]);
62626305
}

apps/oxlint/src-js/generated/types.d.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ export type Declaration =
432432
| TSInterfaceDeclaration
433433
| TSEnumDeclaration
434434
| TSModuleDeclaration
435+
| TSGlobalDeclaration
435436
| TSImportEqualsDeclaration;
436437

437438
export interface VariableDeclaration extends Span {
@@ -1479,11 +1480,21 @@ export interface TSModuleDeclaration extends Span {
14791480
body: TSModuleBlock | null;
14801481
kind: TSModuleDeclarationKind;
14811482
declare: boolean;
1482-
global: boolean;
1483+
global: false;
14831484
parent: Node;
14841485
}
14851486

1486-
export type TSModuleDeclarationKind = 'global' | 'module' | 'namespace';
1487+
export type TSModuleDeclarationKind = 'module' | 'namespace';
1488+
1489+
export interface TSGlobalDeclaration extends Span {
1490+
type: 'TSModuleDeclaration';
1491+
id: IdentifierName;
1492+
body: TSModuleBlock;
1493+
kind: 'global';
1494+
declare: boolean;
1495+
global: true;
1496+
parent: Node;
1497+
}
14871498

14881499
export interface TSModuleBlock extends Span {
14891500
type: 'TSModuleBlock';
@@ -1865,6 +1876,7 @@ export type Node =
18651876
| TSInterfaceHeritage
18661877
| TSTypePredicate
18671878
| TSModuleDeclaration
1879+
| TSGlobalDeclaration
18681880
| TSModuleBlock
18691881
| TSTypeLiteral
18701882
| TSInferType

apps/oxlint/src-js/generated/visitor.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,8 @@ export interface VisitorObject {
330330
'TSMethodSignature:exit'?: (node: ESTree.TSMethodSignature) => void;
331331
TSModuleBlock?: (node: ESTree.TSModuleBlock) => void;
332332
'TSModuleBlock:exit'?: (node: ESTree.TSModuleBlock) => void;
333-
TSModuleDeclaration?: (node: ESTree.TSModuleDeclaration) => void;
334-
'TSModuleDeclaration:exit'?: (node: ESTree.TSModuleDeclaration) => void;
333+
TSModuleDeclaration?: (node: ESTree.TSModuleDeclaration | ESTree.TSGlobalDeclaration) => void;
334+
'TSModuleDeclaration:exit'?: (node: ESTree.TSModuleDeclaration | ESTree.TSGlobalDeclaration) => void;
335335
TSNamedTupleMember?: (node: ESTree.TSNamedTupleMember) => void;
336336
'TSNamedTupleMember:exit'?: (node: ESTree.TSNamedTupleMember) => void;
337337
TSNamespaceExportDeclaration?: (node: ESTree.TSNamespaceExportDeclaration) => void;

crates/oxc_ast/src/ast/js.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,8 @@ pub enum Declaration<'a> {
11671167
TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 36,
11681168
TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 37,
11691169
TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 38,
1170-
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 39,
1170+
TSGlobalDeclaration(Box<'a, TSGlobalDeclaration<'a>>) = 39,
1171+
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40,
11711172
}
11721173

11731174
/// Macro for matching `Declaration`'s variants.
@@ -1181,6 +1182,7 @@ macro_rules! match_declaration {
11811182
| $ty::TSInterfaceDeclaration(_)
11821183
| $ty::TSEnumDeclaration(_)
11831184
| $ty::TSModuleDeclaration(_)
1185+
| $ty::TSGlobalDeclaration(_)
11841186
| $ty::TSImportEqualsDeclaration(_)
11851187
};
11861188
}

crates/oxc_ast/src/ast/macros.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,9 @@ macro_rules! inherit_variants {
448448
/// Inherited from [`Declaration`]
449449
TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 38,
450450
/// Inherited from [`Declaration`]
451-
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 39,
451+
TSGlobalDeclaration(Box<'a, TSGlobalDeclaration<'a>>) = 39,
452+
/// Inherited from [`Declaration`]
453+
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40,
452454

453455
$($rest)*
454456
}
@@ -471,6 +473,7 @@ macro_rules! inherit_variants {
471473
TSInterfaceDeclaration,
472474
TSEnumDeclaration,
473475
TSModuleDeclaration,
476+
TSGlobalDeclaration,
474477
TSImportEqualsDeclaration,
475478
]
476479
);

crates/oxc_ast/src/ast/ts.rs

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,61 +1167,42 @@ pub enum TSTypePredicateName<'a> {
11671167
///
11681168
/// ## Examples
11691169
/// ```ts
1170-
/// declare module 'foo' {
1170+
/// declare module 'foo' { }
11711171
/// // kind ^^^^^^ ^^^^^ id
1172-
/// }
11731172
/// ```
11741173
///
11751174
/// ```ts
11761175
/// namespace Foo { }
11771176
/// declare namespace Bar { }
1178-
/// ```
1179-
///
1180-
/// ```ts
1181-
/// declare global {
1182-
/// interface Window {
1183-
/// customProp: string;
1184-
/// }
1185-
/// }
1177+
/// // kind ^^^^^^^^^ ^^^ id
11861178
/// ```
11871179
///
11881180
/// ## References
11891181
/// * [TypeScript Handbook - Namespaces](https://www.typescriptlang.org/docs/handbook/2/modules.html#namespaces)
11901182
/// * [TypeScript Handbook - Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)
1191-
/// * [TypeScript Handbook - Global Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation)
11921183
#[ast(visit)]
11931184
#[scope(
11941185
flags = ScopeFlags::TsModuleBlock,
11951186
strict_if = self.body.as_ref().is_some_and(TSModuleDeclarationBody::has_use_strict_directive),
11961187
)]
11971188
#[derive(Debug)]
11981189
#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree, UnstableAddress)]
1199-
#[estree(
1200-
via = TSModuleDeclarationConverter,
1201-
add_fields(global = TSModuleDeclarationGlobal),
1202-
)]
1190+
#[estree(via = TSModuleDeclarationConverter, add_fields(global = False))]
12031191
pub struct TSModuleDeclaration<'a> {
12041192
pub span: Span,
12051193
/// The name of the module/namespace being declared.
1206-
///
1207-
/// Note that for `declare global {}`, no symbol will be created for the module name.
12081194
#[estree(ts_type = "BindingIdentifier | StringLiteral | TSQualifiedName")]
12091195
pub id: TSModuleDeclarationName<'a>,
12101196
#[scope(enter_before)]
12111197
#[estree(ts_type = "TSModuleBlock | null")]
12121198
pub body: Option<TSModuleDeclarationBody<'a>>,
12131199
/// The keyword used to define this module declaration.
12141200
///
1215-
/// Helps discriminate between global overrides vs module declarations vs namespace
1216-
/// declarations.
1217-
///
12181201
/// ```ts
12191202
/// namespace Foo {}
12201203
/// ^^^^^^^^^
12211204
/// module 'foo' {}
12221205
/// ^^^^^^
1223-
/// declare global {}
1224-
/// ^^^^^^
12251206
/// ```
12261207
pub kind: TSModuleDeclarationKind,
12271208
pub declare: bool,
@@ -1232,12 +1213,10 @@ pub struct TSModuleDeclaration<'a> {
12321213
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12331214
#[generate_derive(CloneIn, Dummy, ContentEq, ESTree)]
12341215
pub enum TSModuleDeclarationKind {
1235-
/// `declare global {}`
1236-
Global = 0,
1237-
/// `declare module 'foo' {}`
1238-
Module = 1,
1216+
/// `module Foo {}`, `declare module 'foo' {}`
1217+
Module = 0,
12391218
/// `namespace Foo {}`
1240-
Namespace = 2,
1219+
Namespace = 1,
12411220
}
12421221

12431222
/// The name of a TypeScript [namespace or module declaration](TSModuleDeclaration).
@@ -1278,6 +1257,40 @@ pub enum TSModuleDeclarationBody<'a> {
12781257
TSModuleBlock(Box<'a, TSModuleBlock<'a>>) = 1,
12791258
}
12801259

1260+
/// TypeScript Global Declaration
1261+
///
1262+
/// ## Examples
1263+
/// ```ts
1264+
/// declare global {
1265+
/// interface Window {
1266+
/// customProp: string;
1267+
/// }
1268+
/// }
1269+
/// ```
1270+
///
1271+
/// ## References
1272+
/// * [TypeScript Handbook - Global Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation)
1273+
#[ast(visit)]
1274+
// Directives are not allowed in `global {}` blocks, so no need for `strict_if` condition
1275+
#[scope(flags = ScopeFlags::TsModuleBlock)]
1276+
#[derive(Debug)]
1277+
#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree, UnstableAddress)]
1278+
#[estree(
1279+
rename = "TSModuleDeclaration",
1280+
add_fields(id = TSGlobalDeclarationId, kind = Global, global = True),
1281+
field_order(id, body, kind, declare, global, span),
1282+
)]
1283+
pub struct TSGlobalDeclaration<'a> {
1284+
pub span: Span,
1285+
/// Span of `global` keyword
1286+
#[estree(skip)]
1287+
pub global_span: Span,
1288+
pub body: TSModuleBlock<'a>,
1289+
pub declare: bool,
1290+
pub scope_id: Cell<Option<ScopeId>>,
1291+
}
1292+
1293+
/// Body block of a [`TSModuleDeclaration`] or [`TSGlobalDeclaration`].
12811294
#[ast(visit)]
12821295
#[derive(Debug)]
12831296
#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree, UnstableAddress)]

crates/oxc_ast/src/ast_impl/js.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,7 @@ impl<'a> Declaration<'a> {
11131113
/// const x = 1; // None. may change in the future.
11141114
/// class Foo {} // Some(IdentifierReference { name: "Foo", .. })
11151115
/// enum Bar {} // Some(IdentifierReference { name: "Bar", .. })
1116+
/// declare global {} // None
11161117
/// ```
11171118
pub fn id(&self) -> Option<&BindingIdentifier<'a>> {
11181119
match self {
@@ -1129,7 +1130,7 @@ impl<'a> Declaration<'a> {
11291130
None
11301131
}
11311132
}
1132-
Declaration::VariableDeclaration(_) => None,
1133+
Declaration::TSGlobalDeclaration(_) | Declaration::VariableDeclaration(_) => None,
11331134
}
11341135
}
11351136

@@ -1142,6 +1143,7 @@ impl<'a> Declaration<'a> {
11421143
Declaration::TSEnumDeclaration(decl) => decl.declare,
11431144
Declaration::TSTypeAliasDeclaration(decl) => decl.declare,
11441145
Declaration::TSModuleDeclaration(decl) => decl.declare,
1146+
Declaration::TSGlobalDeclaration(decl) => decl.declare,
11451147
Declaration::TSInterfaceDeclaration(decl) => decl.declare,
11461148
Declaration::TSImportEqualsDeclaration(_) => false,
11471149
}

crates/oxc_ast/src/ast_impl/ts.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,16 +186,10 @@ impl TSModuleDeclaration<'_> {
186186
}
187187

188188
impl TSModuleDeclarationKind {
189-
/// Returns `true` for `declare global { ... }`
190-
pub fn is_global(self) -> bool {
191-
matches!(self, TSModuleDeclarationKind::Global)
192-
}
193-
194189
/// Declaration keyword as a string, identical to how it would appear in the
195190
/// source code.
196191
pub fn as_str(self) -> &'static str {
197192
match self {
198-
Self::Global => "global",
199193
Self::Module => "module",
200194
Self::Namespace => "namespace",
201195
}

crates/oxc_ast/src/ast_kind_impl.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl<'a> AstKind<'a> {
3131
pub fn is_declaration(self) -> bool {
3232
matches!(self, Self::Function(func) if func.is_declaration())
3333
|| matches!(self, Self::Class(class) if class.is_declaration())
34-
|| matches!(self, Self::TSEnumDeclaration(_) | Self::TSModuleDeclaration(_)
34+
|| matches!(self, Self::TSEnumDeclaration(_) | Self::TSModuleDeclaration(_) | Self::TSGlobalDeclaration(_)
3535
| Self::VariableDeclaration(_) | Self::TSInterfaceDeclaration(_)
3636
| Self::TSTypeAliasDeclaration(_) | Self::TSImportEqualsDeclaration(_) | Self::PropertyDefinition(_)
3737
) || self.is_module_declaration()
@@ -570,6 +570,7 @@ impl AstKind<'_> {
570570
Self::TSInterfaceDeclaration(_) => "TSInterfaceDeclaration".into(),
571571
Self::TSInterfaceHeritage(_) => "TSInterfaceHeritage".into(),
572572
Self::TSModuleDeclaration(m) => format!("TSModuleDeclaration({})", m.id).into(),
573+
Self::TSGlobalDeclaration(_) => "TSGlobalDeclaration".into(),
573574
Self::TSTypeAliasDeclaration(_) => "TSTypeAliasDeclaration".into(),
574575
Self::TSTypeAnnotation(_) => "TSTypeAnnotation".into(),
575576
Self::TSTypeQuery(_) => "TSTypeQuery".into(),

0 commit comments

Comments
 (0)