Skip to content

Commit e4c1e03

Browse files
authored
fix(typescript): Allow references to the global Symbol in computed property names under isolatedDeclarations (#9869)
fixes #9859 ref: microsoft/TypeScript#58771 Pass `unresolved_mark` to FastDts so it can check whether a `Symbol` ident refs to global Symbol.
1 parent 606ffe5 commit e4c1e03

File tree

13 files changed

+206
-7
lines changed

13 files changed

+206
-7
lines changed

.changeset/long-turtles-grow.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc: major
3+
swc_typescript: major
4+
---
5+
6+
fix(typescript): Allow references to the global Symbol in computed property names under isolatedDeclarations

crates/swc/benches/isolated_declarations.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ fn bench_isolated_declarations(criterion: &mut Criterion) {
3636
let internal_annotations = FastDts::get_internal_annotations(&comments);
3737
let mut checker = FastDts::new(
3838
fm.name.clone(),
39+
unresolved_mark,
3940
FastDtsOptions {
4041
internal_annotations: Some(internal_annotations),
4142
},

crates/swc/src/config/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@ impl Options {
812812
.into_bool(),
813813
codegen_inline_script,
814814
emit_isolated_dts: experimental.emit_isolated_dts.into_bool(),
815+
unresolved_mark,
815816
resolver,
816817
})
817818
}
@@ -1124,6 +1125,7 @@ pub struct BuiltInput<P: Pass> {
11241125
pub codegen_inline_script: bool,
11251126

11261127
pub emit_isolated_dts: bool,
1128+
pub unresolved_mark: Mark,
11271129
pub resolver: Option<(FileName, Arc<dyn ImportResolver>)>,
11281130
}
11291131

@@ -1156,6 +1158,7 @@ where
11561158
emit_assert_for_import_attributes: self.emit_assert_for_import_attributes,
11571159
codegen_inline_script: self.codegen_inline_script,
11581160
emit_isolated_dts: self.emit_isolated_dts,
1161+
unresolved_mark: self.unresolved_mark,
11591162
resolver: self.resolver,
11601163
}
11611164
}

crates/swc/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,8 @@ impl Compiler {
983983
let trailing = std::rc::Rc::new(RefCell::new(trailing.clone()));
984984

985985
let comments = SingleThreadedComments::from_leading_and_trailing(leading, trailing);
986-
let mut checker = FastDts::new(fm.name.clone(), Default::default());
986+
let mut checker =
987+
FastDts::new(fm.name.clone(), config.unresolved_mark, Default::default());
987988
let mut program = program.clone();
988989

989990
if let Some((base, resolver)) = config.resolver {

crates/swc_typescript/examples/isolated_declarations.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub fn main() {
3434
let internal_annotations = FastDts::get_internal_annotations(&comments);
3535
let mut checker = FastDts::new(
3636
fm.name.clone(),
37+
unresolved_mark,
3738
FastDtsOptions {
3839
internal_annotations: Some(internal_annotations),
3940
},

crates/swc_typescript/src/fast_dts/class.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,12 +501,42 @@ impl FastDts {
501501

502502
pub(crate) fn report_property_key(&mut self, key: &PropName) -> bool {
503503
if let Some(computed) = key.as_computed() {
504+
let is_symbol = self.is_global_symbol_object(&computed.expr);
504505
let is_literal = Self::is_literal(&computed.expr);
505-
if !is_literal {
506+
if !is_symbol && !is_literal {
506507
self.computed_property_name(key.span());
507508
}
508-
return !is_literal;
509+
return !is_symbol && !is_literal;
509510
}
510511
false
511512
}
513+
514+
pub(crate) fn is_global_symbol_object(&self, expr: &Expr) -> bool {
515+
let Some(obj) = (match expr {
516+
Expr::Member(member) => Some(&member.obj),
517+
Expr::OptChain(opt_chain) => opt_chain.base.as_member().map(|member| &member.obj),
518+
_ => None,
519+
}) else {
520+
return false;
521+
};
522+
523+
// https://github.com/microsoft/TypeScript/blob/cbac1ddfc73ca3b9d8741c1b51b74663a0f24695/src/compiler/transformers/declarations.ts#L1011
524+
if let Some(ident) = obj.as_ident() {
525+
// Exactly `Symbol.something` and `Symbol` either does not resolve
526+
// or definitely resolves to the global Symbol
527+
return ident.sym.as_str() == "Symbol" && ident.ctxt.has_mark(self.unresolved_mark);
528+
}
529+
530+
if let Some(member_expr) = obj.as_member() {
531+
// Exactly `globalThis.Symbol.something` and `globalThis` resolves
532+
// to the global `globalThis`
533+
if let Some(ident) = member_expr.obj.as_ident() {
534+
return ident.sym.as_str() == "globalThis"
535+
&& ident.ctxt.has_mark(self.unresolved_mark)
536+
&& member_expr.prop.is_ident_with("Symbol");
537+
}
538+
}
539+
540+
false
541+
}
512542
}

crates/swc_typescript/src/fast_dts/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use std::{borrow::Cow, mem::take, sync::Arc};
33
use rustc_hash::{FxHashMap, FxHashSet};
44
use swc_atoms::Atom;
55
use swc_common::{
6-
comments::SingleThreadedComments, util::take::Take, BytePos, FileName, Span, Spanned, DUMMY_SP,
6+
comments::SingleThreadedComments, util::take::Take, BytePos, FileName, Mark, Span, Spanned,
7+
DUMMY_SP,
78
};
89
use swc_ecma_ast::{
910
BindingIdent, Decl, DefaultDecl, ExportDefaultExpr, Id, Ident, ImportSpecifier, ModuleDecl,
@@ -38,6 +39,7 @@ mod visitors;
3839
/// The original code is MIT licensed.
3940
pub struct FastDts {
4041
filename: Arc<FileName>,
42+
unresolved_mark: Mark,
4143
diagnostics: Vec<DtsIssue>,
4244
// states
4345
id_counter: u32,
@@ -53,10 +55,11 @@ pub struct FastDtsOptions {
5355

5456
/// Diagnostics
5557
impl FastDts {
56-
pub fn new(filename: Arc<FileName>, options: FastDtsOptions) -> Self {
58+
pub fn new(filename: Arc<FileName>, unresolved_mark: Mark, options: FastDtsOptions) -> Self {
5759
let internal_annotations = options.internal_annotations;
5860
Self {
5961
filename,
62+
unresolved_mark,
6063
diagnostics: Vec::new(),
6164
id_counter: 0,
6265
is_top_level: true,

crates/swc_typescript/tests/deno_test.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
//! Tests copied from deno
22
//! Make some changes to align with tsc
33
4+
use swc_common::Mark;
45
use swc_ecma_ast::EsVersion;
56
use swc_ecma_codegen::to_code;
67
use swc_ecma_parser::{parse_file_as_program, Syntax, TsSyntax};
8+
use swc_ecma_transforms_base::resolver;
79
use swc_typescript::fast_dts::FastDts;
810

911
#[track_caller]
@@ -14,8 +16,8 @@ fn transform_dts_test(source: &str, expected: &str) {
1416
source.to_string(),
1517
);
1618

17-
let mut checker = FastDts::new(fm.name.clone(), Default::default());
18-
19+
let unresolved_mark = Mark::new();
20+
let top_level_mark = Mark::new();
1921
let mut program = parse_file_as_program(
2022
&fm,
2123
Syntax::Typescript(TsSyntax {
@@ -25,8 +27,11 @@ fn transform_dts_test(source: &str, expected: &str) {
2527
None,
2628
&mut Vec::new(),
2729
)
30+
.map(|program| program.apply(resolver(unresolved_mark, top_level_mark, true)))
2831
.unwrap();
2932

33+
let mut checker = FastDts::new(fm.name.clone(), unresolved_mark, Default::default());
34+
3035
let _issues = checker.transform(&mut program);
3136

3237
let code = to_code(&program);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
```==================== .D.TS ====================
2+
3+
export declare class NumberRange implements Iterable<number> {
4+
private start;
5+
private end;
6+
constructor(start: number, end: number);
7+
[Symbol.iterator](): Iterator<number>;
8+
}
9+
10+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export class NumberRange implements Iterable<number> {
2+
private start: number;
3+
private end: number;
4+
5+
constructor(start: number, end: number) {
6+
this.start = start;
7+
this.end = end;
8+
}
9+
10+
[Symbol.iterator](): Iterator<number> {
11+
let current = this.start;
12+
const end = this.end;
13+
14+
return {
15+
next(): IteratorResult<number> {
16+
if (current <= end) {
17+
return { value: current++, done: false };
18+
} else {
19+
return { value: undefined, done: true };
20+
}
21+
},
22+
};
23+
}
24+
}

0 commit comments

Comments
 (0)