Skip to content

Commit 1a37e3f

Browse files
committed
feat(semantic): add SymbolFlags::Ambient for declaration with declare modifier (#10355)
Part of #7951. After we have a `SymbolFlags::Ambient` within a Symbol that is declared by the `declare` modifier, we can determine whether to remove an `ExportSpecifier` by tracing its referent and examining its `SymbolFlags`.
1 parent dd2aef0 commit 1a37e3f

File tree

8 files changed

+76
-41
lines changed

8 files changed

+76
-41
lines changed

crates/oxc_semantic/src/binder.rs

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ pub trait Binder<'a> {
2020

2121
impl<'a> Binder<'a> for VariableDeclarator<'a> {
2222
fn bind(&self, builder: &mut SemanticBuilder<'a>) {
23-
let (includes, excludes) = match self.kind {
23+
let is_declare = builder
24+
.nodes
25+
.parent_kind(builder.current_node_id)
26+
.is_some_and(|kind| matches!(kind, AstKind::VariableDeclaration(decl) if decl.declare));
27+
28+
let (mut includes, excludes) = match self.kind {
2429
VariableDeclarationKind::Const
2530
| VariableDeclarationKind::Using
2631
| VariableDeclarationKind::AwaitUsing => (
@@ -35,6 +40,10 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> {
3540
}
3641
};
3742

43+
if is_declare {
44+
includes |= SymbolFlags::Ambient;
45+
}
46+
3847
if self.kind.is_lexical() {
3948
self.id.bound_names(&mut |ident| {
4049
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
@@ -117,13 +126,14 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> {
117126

118127
impl<'a> Binder<'a> for Class<'a> {
119128
fn bind(&self, builder: &mut SemanticBuilder) {
129+
let includes = if self.declare {
130+
SymbolFlags::Class | SymbolFlags::Ambient
131+
} else {
132+
SymbolFlags::Class
133+
};
120134
let Some(ident) = &self.id else { return };
121-
let symbol_id = builder.declare_symbol(
122-
ident.span,
123-
&ident.name,
124-
SymbolFlags::Class,
125-
SymbolFlags::ClassExcludes,
126-
);
135+
let symbol_id =
136+
builder.declare_symbol(ident.span, &ident.name, includes, SymbolFlags::ClassExcludes);
127137
ident.symbol_id.set(Some(symbol_id));
128138
}
129139
}
@@ -152,30 +162,32 @@ fn is_function_part_of_if_statement(function: &Function, builder: &SemanticBuild
152162

153163
impl<'a> Binder<'a> for Function<'a> {
154164
fn bind(&self, builder: &mut SemanticBuilder) {
165+
let includes = if self.declare {
166+
SymbolFlags::Function | SymbolFlags::Ambient
167+
} else {
168+
SymbolFlags::Function
169+
};
170+
155171
if let Some(ident) = &self.id {
156172
if is_function_part_of_if_statement(self, builder) {
157173
let symbol_id = builder.scoping.create_symbol(
158174
ident.span,
159175
ident.name.into(),
160-
SymbolFlags::Function,
176+
includes,
161177
ScopeId::new(u32::MAX - 1), // Not bound to any scope.
162178
builder.current_node_id,
163179
);
164180
ident.symbol_id.set(Some(symbol_id));
165181
} else {
166-
let symbol_id = builder.declare_symbol(
167-
ident.span,
168-
&ident.name,
169-
SymbolFlags::Function,
170-
if builder.source_type.is_typescript() {
171-
SymbolFlags::FunctionExcludes
172-
} else {
173-
// `var x; function x() {}` is valid in non-strict mode, but `TypeScript`
174-
// doesn't care about non-strict mode, so we need to exclude this,
175-
// and further check in checker.
176-
SymbolFlags::FunctionExcludes - SymbolFlags::FunctionScopedVariable
177-
},
178-
);
182+
let excludes = if builder.source_type.is_typescript() {
183+
SymbolFlags::FunctionExcludes
184+
} else {
185+
// `var x; function x() {}` is valid in non-strict mode, but `TypeScript`
186+
// doesn't care about non-strict mode, so we need to exclude this,
187+
// and further check in checker.
188+
SymbolFlags::FunctionExcludes - SymbolFlags::FunctionScopedVariable
189+
};
190+
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
179191
ident.symbol_id.set(Some(symbol_id));
180192
}
181193
}
@@ -336,10 +348,15 @@ impl<'a> Binder<'a> for TSImportEqualsDeclaration<'a> {
336348

337349
impl<'a> Binder<'a> for TSTypeAliasDeclaration<'a> {
338350
fn bind(&self, builder: &mut SemanticBuilder) {
351+
let includes = if self.declare {
352+
SymbolFlags::TypeAlias | SymbolFlags::Ambient
353+
} else {
354+
SymbolFlags::TypeAlias
355+
};
339356
let symbol_id = builder.declare_symbol(
340357
self.id.span,
341358
&self.id.name,
342-
SymbolFlags::TypeAlias,
359+
includes,
343360
SymbolFlags::TypeAliasExcludes,
344361
);
345362
self.id.symbol_id.set(Some(symbol_id));
@@ -348,10 +365,15 @@ impl<'a> Binder<'a> for TSTypeAliasDeclaration<'a> {
348365

349366
impl<'a> Binder<'a> for TSInterfaceDeclaration<'a> {
350367
fn bind(&self, builder: &mut SemanticBuilder) {
368+
let includes = if self.declare {
369+
SymbolFlags::Interface | SymbolFlags::Ambient
370+
} else {
371+
SymbolFlags::Interface
372+
};
351373
let symbol_id = builder.declare_symbol(
352374
self.id.span,
353375
&self.id.name,
354-
SymbolFlags::Interface,
376+
includes,
355377
SymbolFlags::InterfaceExcludes,
356378
);
357379
self.id.symbol_id.set(Some(symbol_id));
@@ -361,11 +383,11 @@ impl<'a> Binder<'a> for TSInterfaceDeclaration<'a> {
361383
impl<'a> Binder<'a> for TSEnumDeclaration<'a> {
362384
fn bind(&self, builder: &mut SemanticBuilder) {
363385
let is_const = self.r#const;
364-
let includes = if is_const { SymbolFlags::ConstEnum } else { SymbolFlags::RegularEnum };
365-
let excludes = if is_const {
366-
SymbolFlags::ConstEnumExcludes
386+
let includes = if self.declare { SymbolFlags::Ambient } else { SymbolFlags::empty() };
387+
let (includes, excludes) = if is_const {
388+
(SymbolFlags::ConstEnum | includes, SymbolFlags::ConstEnumExcludes)
367389
} else {
368-
SymbolFlags::RegularEnumExcludes
390+
(SymbolFlags::RegularEnum | includes, SymbolFlags::RegularEnumExcludes)
369391
};
370392
let symbol_id = builder.declare_symbol(self.id.span, &self.id.name, includes, excludes);
371393
self.id.symbol_id.set(Some(symbol_id));
@@ -392,16 +414,16 @@ impl<'a> Binder<'a> for TSModuleDeclaration<'a> {
392414
let TSModuleDeclarationName::Identifier(id) = &self.id else { return };
393415
let instantiated =
394416
get_module_instance_state(builder, self, builder.current_node_id).is_instantiated();
395-
let (includes, excludes) = if instantiated {
417+
let (mut includes, excludes) = if instantiated {
396418
(SymbolFlags::ValueModule, SymbolFlags::ValueModuleExcludes)
397419
} else {
398420
(SymbolFlags::NameSpaceModule, SymbolFlags::NamespaceModuleExcludes)
399421
};
400422

401-
// At declaration time a module has no value declaration it is only when a value declaration
402-
// is made inside a the scope of a module that the symbol is modified
403-
let ambient = if self.declare { SymbolFlags::Ambient } else { SymbolFlags::None };
404-
let symbol_id = builder.declare_symbol(id.span, &id.name, includes | ambient, excludes);
423+
if self.declare {
424+
includes |= SymbolFlags::Ambient;
425+
}
426+
let symbol_id = builder.declare_symbol(id.span, &id.name, includes, excludes);
405427

406428
id.set_symbol_id(symbol_id);
407429
}

crates/oxc_semantic/tests/fixtures/typescript-eslint/decorators/class-deco-with-object-param.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/decorators/clas
4141
"node": "Program",
4242
"symbols": [
4343
{
44-
"flags": "SymbolFlags(Function)",
44+
"flags": "SymbolFlags(Function | Ambient)",
4545
"id": 0,
4646
"name": "deco",
4747
"node": "Function(deco)",

crates/oxc_semantic/tests/fixtures/typescript-eslint/jsx/this-jsxidentifier.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/jsx/this-jsxide
3333
"node": "Program",
3434
"symbols": [
3535
{
36-
"flags": "SymbolFlags(BlockScopedVariable | ConstVariable)",
36+
"flags": "SymbolFlags(BlockScopedVariable | ConstVariable | Ambient)",
3737
"id": 0,
3838
"name": "React",
3939
"node": "VariableDeclarator(React)",

crates/oxc_semantic/tests/fixtures/typescript-eslint/type-declaration/literal-type3.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/type-declaratio
6868
]
6969
},
7070
{
71-
"flags": "SymbolFlags(Function)",
71+
"flags": "SymbolFlags(Function | Ambient)",
7272
"id": 2,
7373
"name": "setAlignment",
7474
"node": "Function(setAlignment)",

crates/oxc_syntax/src/symbol.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,12 @@ bitflags! {
125125
const TypeParameter = 1 << 13;
126126
const NameSpaceModule = 1 << 14;
127127
const ValueModule = 1 << 15;
128-
// In a dts file or there is a declare flag
128+
/// Declared with `declare` modifier, like `declare function x() {}`.
129+
//
130+
// This flag is not part of TypeScript's `SymbolFlags`, it comes from TypeScript's `NodeFlags`. We introduced it into
131+
// here because `NodeFlags` is incomplete and we only can access to `NodeFlags` in the Semantic, but we also need to
132+
// access it in the Transformer.
133+
// https://github.com/microsoft/TypeScript/blob/15392346d05045742e653eab5c87538ff2a3c863/src/compiler/types.ts#L819-L820
129134
const Ambient = 1 << 16;
130135

131136
const Enum = Self::ConstEnum.bits() | Self::RegularEnum.bits();
@@ -245,6 +250,11 @@ impl SymbolFlags {
245250
self.contains(Self::TypeImport)
246251
}
247252

253+
#[inline]
254+
pub fn is_ambient(&self) -> bool {
255+
self.contains(Self::Ambient)
256+
}
257+
248258
/// If true, then the symbol can be referenced by a type reference
249259
#[inline]
250260
pub fn can_be_referenced_by_type(&self) -> bool {

tasks/coverage/snapshots/semantic_babel.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1458,7 +1458,7 @@ semantic error: Scope children mismatch:
14581458
after transform: ScopeId(0): [ScopeId(1), ScopeId(2)]
14591459
rebuilt : ScopeId(0): [ScopeId(1)]
14601460
Symbol flags mismatch for "C":
1461-
after transform: SymbolId(0): SymbolFlags(Class | Function)
1461+
after transform: SymbolId(0): SymbolFlags(Class | Function | Ambient)
14621462
rebuilt : SymbolId(0): SymbolFlags(Function)
14631463
Symbol span mismatch for "C":
14641464
after transform: SymbolId(0): Span { start: 14, end: 15 }

tasks/coverage/snapshots/semantic_typescript.snap

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ Scope children mismatch:
379379
after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3), ScopeId(4), ScopeId(5)]
380380
rebuilt : ScopeId(0): [ScopeId(1)]
381381
Symbol flags mismatch for "D":
382-
after transform: SymbolId(3): SymbolFlags(Class | ValueModule)
382+
after transform: SymbolId(3): SymbolFlags(Class | ValueModule | Ambient)
383383
rebuilt : SymbolId(0): SymbolFlags(BlockScopedVariable)
384384
Symbol span mismatch for "D":
385385
after transform: SymbolId(3): Span { start: 97, end: 98 }
@@ -398,7 +398,7 @@ semantic error: Scope children mismatch:
398398
after transform: ScopeId(0): [ScopeId(1), ScopeId(2)]
399399
rebuilt : ScopeId(0): [ScopeId(1)]
400400
Symbol flags mismatch for "foo":
401-
after transform: SymbolId(0): SymbolFlags(Class | Function)
401+
after transform: SymbolId(0): SymbolFlags(Class | Function | Ambient)
402402
rebuilt : SymbolId(0): SymbolFlags(Function)
403403
Symbol span mismatch for "foo":
404404
after transform: SymbolId(0): Span { start: 14, end: 17 }
@@ -46653,6 +46653,9 @@ tasks/coverage/typescript/tests/cases/conformance/jsx/jsxReactTestSuite.tsx
4665346653
semantic error: Bindings mismatch:
4665446654
after transform: ScopeId(0): ["Child", "Component", "Composite", "Composite2", "Namespace", "React", "_jsxFileName", "_objectSpread", "_reactJsxRuntime", "bar", "foo", "hasOwnProperty", "x", "y", "z"]
4665546655
rebuilt : ScopeId(0): ["_jsxFileName", "_objectSpread", "_reactJsxRuntime", "x"]
46656+
Symbol flags mismatch for "x":
46657+
after transform: SymbolId(9): SymbolFlags(FunctionScopedVariable | Ambient)
46658+
rebuilt : SymbolId(3): SymbolFlags(FunctionScopedVariable)
4665646659
Symbol span mismatch for "x":
4665746660
after transform: SymbolId(9): Span { start: 231, end: 236 }
4665846661
rebuilt : SymbolId(3): Span { start: 537, end: 538 }

tasks/transform_conformance/snapshots/babel.snap.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,7 +1705,7 @@ Scope children mismatch:
17051705
after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3), ScopeId(4)]
17061706
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)]
17071707
Symbol flags mismatch for "Signal":
1708-
after transform: SymbolId(0): SymbolFlags(Class | Function)
1708+
after transform: SymbolId(0): SymbolFlags(Class | Function | Ambient)
17091709
rebuilt : SymbolId(0): SymbolFlags(Function)
17101710
Symbol span mismatch for "Signal":
17111711
after transform: SymbolId(0): Span { start: 14, end: 20 }
@@ -1717,7 +1717,7 @@ Symbol redeclarations mismatch for "Signal":
17171717
after transform: SymbolId(0): [Span { start: 14, end: 20 }, Span { start: 54, end: 60 }]
17181718
rebuilt : SymbolId(0): []
17191719
Symbol flags mismatch for "Signal2":
1720-
after transform: SymbolId(3): SymbolFlags(Class | Function)
1720+
after transform: SymbolId(3): SymbolFlags(Class | Function | Ambient)
17211721
rebuilt : SymbolId(2): SymbolFlags(Function)
17221722
Symbol reference IDs mismatch for "Signal2":
17231723
after transform: SymbolId(3): [ReferenceId(4), ReferenceId(7)]

0 commit comments

Comments
 (0)