diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index a0471d958728fc..ae3327211f08a8 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -1325,6 +1325,9 @@ pub enum TSTypePredicateName<'a> { pub struct TSModuleDeclaration<'a> { #[serde(flatten)] pub span: Span, + /// The name of the module/namespace being declared. + /// + /// Note that for `declare global {}`, no symbol will be created for the module name. pub id: TSModuleDeclarationName<'a>, #[scope(enter_before)] pub body: Option>, diff --git a/crates/oxc_ast/src/generated/ast_builder.rs b/crates/oxc_ast/src/generated/ast_builder.rs index f89659aab65829..41f53072c2cc6d 100644 --- a/crates/oxc_ast/src/generated/ast_builder.rs +++ b/crates/oxc_ast/src/generated/ast_builder.rs @@ -4417,7 +4417,7 @@ impl<'a> AstBuilder<'a> { /// /// ## Parameters /// - span: The [`Span`] covering this node - /// - id + /// - id: The name of the module/namespace being declared. /// - body /// - kind: The keyword used to define this module declaration. /// - declare @@ -11381,7 +11381,7 @@ impl<'a> AstBuilder<'a> { /// /// ## Parameters /// - span: The [`Span`] covering this node - /// - id + /// - id: The name of the module/namespace being declared. /// - body /// - kind: The keyword used to define this module declaration. /// - declare @@ -11403,7 +11403,7 @@ impl<'a> AstBuilder<'a> { /// /// ## Parameters /// - span: The [`Span`] covering this node - /// - id + /// - id: The name of the module/namespace being declared. /// - body /// - kind: The keyword used to define this module declaration. /// - declare @@ -11483,7 +11483,7 @@ impl<'a> AstBuilder<'a> { /// /// ## Parameters /// - span: The [`Span`] covering this node - /// - id + /// - id: The name of the module/namespace being declared. /// - body /// - kind: The keyword used to define this module declaration. /// - declare diff --git a/crates/oxc_semantic/src/binder.rs b/crates/oxc_semantic/src/binder.rs index bae7ff38a68ca4..611449bc6b7bb6 100644 --- a/crates/oxc_semantic/src/binder.rs +++ b/crates/oxc_semantic/src/binder.rs @@ -411,10 +411,16 @@ impl<'a> Binder<'a> for TSEnumMember<'a> { impl<'a> Binder<'a> for TSModuleDeclaration<'a> { fn bind(&self, builder: &mut SemanticBuilder) { + // do not bind `global` for `declare global { ... }` + if matches!(self.kind, TSModuleDeclarationKind::Global) { + return; + } + // At declaration time a module has no value declaration it is only when a value declaration // is made inside a the scope of a module that the symbol is modified let ambient = if self.declare { SymbolFlags::Ambient } else { SymbolFlags::None }; - builder.declare_symbol( + // FIXME: insert symbol_id into TSModuleDeclarationName AST node + let _symbol_id = builder.declare_symbol( self.id.span(), self.id.name().as_str(), SymbolFlags::NameSpaceModule | ambient, diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 75ab363b7df2b7..516e56c32cf23f 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -82,7 +82,7 @@ pub struct SemanticBuilder<'a> { // we need the to know the modules we are inside // and when we reach a value declaration we set it // to value like - pub(crate) namespace_stack: Vec, + pub(crate) namespace_stack: Vec>, current_reference_flags: ReferenceFlags, pub(crate) hoisting_variables: FxHashMap, SymbolId>>, @@ -1847,8 +1847,9 @@ impl<'a> SemanticBuilder<'a> { let symbol_id = self .scope .get_bindings(self.current_scope_id) - .get(module_declaration.id.name().as_str()); - self.namespace_stack.push(*symbol_id.unwrap()); + .get(module_declaration.id.name().as_str()) + .copied(); + self.namespace_stack.push(symbol_id); self.current_symbol_flags -= SymbolFlags::Export; } AstKind::TSTypeAliasDeclaration(type_alias_declaration) => { @@ -2025,11 +2026,15 @@ impl<'a> SemanticBuilder<'a> { fn make_all_namespaces_valuelike(&mut self) { for symbol_id in &self.namespace_stack { + let Some(symbol_id) = *symbol_id else { + continue; + }; + // Ambient modules cannot be value modules - if self.symbols.get_flags(*symbol_id).intersects(SymbolFlags::Ambient) { + if self.symbols.get_flags(symbol_id).intersects(SymbolFlags::Ambient) { continue; } - self.symbols.union_flag(*symbol_id, SymbolFlags::ValueModule); + self.symbols.union_flag(symbol_id, SymbolFlags::ValueModule); } } diff --git a/crates/oxc_semantic/tests/fixtures/typescript-eslint/ts-module/global-augmentation.snap b/crates/oxc_semantic/tests/fixtures/typescript-eslint/ts-module/global-augmentation.snap index 3c959604942b2d..cfc0a7760ddc6f 100644 --- a/crates/oxc_semantic/tests/fixtures/typescript-eslint/ts-module/global-augmentation.snap +++ b/crates/oxc_semantic/tests/fixtures/typescript-eslint/ts-module/global-augmentation.snap @@ -16,14 +16,6 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/ts-module/globa "flags": "ScopeFlags(StrictMode | Top)", "id": 0, "node": "Program", - "symbols": [ - { - "flags": "SymbolFlags(NameSpaceModule | Ambient)", - "id": 0, - "name": "global", - "node": "TSModuleDeclaration(global)", - "references": [] - } - ] + "symbols": [] } ] diff --git a/crates/oxc_semantic/tests/integration/symbols.rs b/crates/oxc_semantic/tests/integration/symbols.rs index d1ca7aab35f530..6114737f08267f 100644 --- a/crates/oxc_semantic/tests/integration/symbols.rs +++ b/crates/oxc_semantic/tests/integration/symbols.rs @@ -449,3 +449,29 @@ fn test_tagged_templates() { .has_number_of_writes(0) .test(); } + +#[test] +fn test_module_like_declarations() { + SemanticTester::ts("namespace A { export const x = 1; }") + .has_root_symbol("A") + .contains_flags(SymbolFlags::NameSpaceModule) + .test(); + + SemanticTester::ts("module A { export const x = 1; }") + .has_root_symbol("A") + .contains_flags(SymbolFlags::NameSpaceModule) + .test(); + + SemanticTester::ts(r#"module "A" { export const x = 1; }"#) + .has_root_symbol("A") + .contains_flags(SymbolFlags::NameSpaceModule) + .test(); + + let test = SemanticTester::ts("declare global { interface Window { x: number; } }"); + let semantic = test.build(); + let global = semantic.symbols().names.iter().find(|name| *name == "global"); + assert!( + global.is_none(), + "A symbol should not be created for global augmentation declarations." + ); +} diff --git a/tasks/coverage/snapshots/semantic_babel.snap b/tasks/coverage/snapshots/semantic_babel.snap index 9930aa1e0ace76..c53a82d161a410 100644 --- a/tasks/coverage/snapshots/semantic_babel.snap +++ b/tasks/coverage/snapshots/semantic_babel.snap @@ -1340,7 +1340,7 @@ rebuilt : ScopeId(0): [] tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/head-declare/input.ts semantic error: Bindings mismatch: -after transform: ScopeId(0): ["M", "N", "global", "m"] +after transform: ScopeId(0): ["M", "N", "m"] rebuilt : ScopeId(0): [] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(3), ScopeId(4), ScopeId(5)] diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap index bc1ed8a9739f11..213b53397e26ae 100644 --- a/tasks/coverage/snapshots/semantic_typescript.snap +++ b/tasks/coverage/snapshots/semantic_typescript.snap @@ -9009,10 +9009,7 @@ after transform: ScopeId(1): ["E", "i"] rebuilt : ScopeId(1): ["i"] tasks/coverage/typescript/tests/cases/compiler/declarationEmitRetainsJsdocyComments.ts -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["Foo", "foo", "global", "someMethod"] -rebuilt : ScopeId(0): ["Foo", "foo", "someMethod"] -Scope children mismatch: +semantic error: Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(4), ScopeId(6)] rebuilt : ScopeId(0): [ScopeId(1), ScopeId(4)] @@ -10734,7 +10731,7 @@ rebuilt : [] tasks/coverage/typescript/tests/cases/compiler/doubleUnderscoreReactNamespace.ts semantic error: Bindings mismatch: -after transform: ScopeId(0): ["__foot", "_jsxFileName", "global", "thing"] +after transform: ScopeId(0): ["__foot", "_jsxFileName", "thing"] rebuilt : ScopeId(0): ["_jsxFileName", "thing"] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] @@ -17286,10 +17283,7 @@ after transform: ReferenceId(2): Some("M") rebuilt : ReferenceId(6): Some("M") tasks/coverage/typescript/tests/cases/compiler/globalFunctionAugmentationOverload.ts -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["global"] -rebuilt : ScopeId(0): [] -Scope children mismatch: +semantic error: Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] rebuilt : ScopeId(0): [] @@ -21003,7 +20997,7 @@ rebuilt : ScopeId(0): [ScopeId(1)] tasks/coverage/typescript/tests/cases/compiler/jsxCallbackWithDestructuring.tsx semantic error: Bindings mismatch: -after transform: ScopeId(0): ["Component", "MyComponent", "RouteProps", "_jsx", "_jsxFileName", "global"] +after transform: ScopeId(0): ["Component", "MyComponent", "RouteProps", "_jsx", "_jsxFileName"] rebuilt : ScopeId(0): ["MyComponent", "_jsx", "_jsxFileName"] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(5), ScopeId(14), ScopeId(15), ScopeId(16)] @@ -23043,7 +23037,7 @@ rebuilt : ScopeId(0): [ScopeId(1)] tasks/coverage/typescript/tests/cases/compiler/moduleAugmentationGlobal1.ts semantic error: Bindings mismatch: -after transform: ScopeId(0): ["A", "global", "x", "y"] +after transform: ScopeId(0): ["A", "x", "y"] rebuilt : ScopeId(0): ["x", "y"] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] @@ -23051,7 +23045,7 @@ rebuilt : ScopeId(0): [] tasks/coverage/typescript/tests/cases/compiler/moduleAugmentationGlobal2.ts semantic error: Bindings mismatch: -after transform: ScopeId(0): ["A", "global", "x", "y"] +after transform: ScopeId(0): ["A", "x", "y"] rebuilt : ScopeId(0): ["x", "y"] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] @@ -23059,17 +23053,14 @@ rebuilt : ScopeId(0): [] tasks/coverage/typescript/tests/cases/compiler/moduleAugmentationGlobal3.ts semantic error: Bindings mismatch: -after transform: ScopeId(0): ["A", "global"] +after transform: ScopeId(0): ["A"] rebuilt : ScopeId(0): [] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] rebuilt : ScopeId(0): [] tasks/coverage/typescript/tests/cases/compiler/moduleAugmentationGlobal4.ts -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["global"] -rebuilt : ScopeId(0): [] -Scope children mismatch: +semantic error: Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] rebuilt : ScopeId(0): [] @@ -37199,10 +37190,7 @@ after transform: [ReferenceId(7), ReferenceId(9), ReferenceId(13)] rebuilt : [ReferenceId(4)] tasks/coverage/typescript/tests/cases/compiler/uniqueSymbolAssignmentOnGlobalAugmentationSuceeds.ts -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["FOO_SYMBOL", "foo", "global"] -rebuilt : ScopeId(0): ["FOO_SYMBOL", "foo"] -Scope children mismatch: +semantic error: Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(3)] rebuilt : ScopeId(0): [ScopeId(1)] Bindings mismatch: @@ -40502,14 +40490,11 @@ after transform: [ReferenceId(0), ReferenceId(11)] rebuilt : [ReferenceId(10)] tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowInstanceofExtendsFunction.ts -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["X", "Y", "global", "x"] -rebuilt : ScopeId(0): ["X", "Y", "x"] -Scope children mismatch: +semantic error: Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(4), ScopeId(5), ScopeId(8), ScopeId(9)] rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(5), ScopeId(6)] Symbol reference IDs mismatch: -after transform: SymbolId(2): [ReferenceId(2), ReferenceId(5), ReferenceId(7), ReferenceId(9)] +after transform: SymbolId(1): [ReferenceId(2), ReferenceId(5), ReferenceId(7), ReferenceId(9)] rebuilt : SymbolId(0): [ReferenceId(2), ReferenceId(6), ReferenceId(8)] tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts @@ -42020,10 +42005,7 @@ semantic error: The only valid meta property for import is import.meta The only valid meta property for import is import.meta tasks/coverage/typescript/tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["global"] -rebuilt : ScopeId(0): [] -Scope children mismatch: +semantic error: Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(3)] rebuilt : ScopeId(0): [ScopeId(1)] @@ -42503,7 +42485,7 @@ rebuilt : [] tasks/coverage/typescript/tests/cases/conformance/es6/Symbols/symbolProperty61.ts semantic error: Bindings mismatch: -after transform: ScopeId(0): ["InteropObservable", "MyObservable", "from", "global", "observable"] +after transform: ScopeId(0): ["InteropObservable", "MyObservable", "from", "observable"] rebuilt : ScopeId(0): ["MyObservable", "from", "observable"] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(3), ScopeId(7), ScopeId(9)] @@ -47533,10 +47515,7 @@ after transform: SymbolId(0): [ReferenceId(0), ReferenceId(1), ReferenceId(2)] rebuilt : SymbolId(0): [ReferenceId(0)] tasks/coverage/typescript/tests/cases/conformance/externalModules/globalAugmentationModuleResolution.ts -semantic error: Bindings mismatch: -after transform: ScopeId(0): ["global"] -rebuilt : ScopeId(0): [] -Scope children mismatch: +semantic error: Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] rebuilt : ScopeId(0): [] diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index edc02b39bd04c8..ad7d6e54af76e3 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -2701,14 +2701,14 @@ Missing SymbolId: X Missing SymbolId: _X Missing ReferenceId: X Missing ReferenceId: X -Bindings mismatch: -after transform: ScopeId(0): ["X", "global", "i18n"] -rebuilt : ScopeId(0): ["X", "i18n"] +Binding symbols mismatch: +after transform: ScopeId(0): [SymbolId(2), SymbolId(4)] +rebuilt : ScopeId(0): [SymbolId(0), SymbolId(3)] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(3), ScopeId(4)] rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)] Binding symbols mismatch: -after transform: ScopeId(3): [SymbolId(4), SymbolId(6)] +after transform: ScopeId(3): [SymbolId(3), SymbolId(5)] rebuilt : ScopeId(1): [SymbolId(1), SymbolId(2)] * namespace/empty-removed/input.ts