Skip to content

Commit 9e6dad9

Browse files
authoredFeb 6, 2024
fix(es/parser): Rescan << as < when parsing type args (#8607)
**Description:** The related issue is a product to the fact that the lexer sees a `<<` token in `fun<<T>...` and therefore parses the type args as an arrow function. This PR adds the handling of the "split" of `<<` when beginning to parse type args. I am open for suggestions, I still find this a bit odd (in `parse_ts_type_args()`): ```rs if is!(p, "<<") { p.input.cut_lshift(); } else { expect!(p, '<'); } ``` **Related issue:** - Closes #7187 - Closes #8209 - Closes #8581
1 parent c3f67ce commit 9e6dad9

File tree

16 files changed

+1210
-2
lines changed

16 files changed

+1210
-2
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"jsc": {
3+
"parser": {
4+
"syntax": "typescript",
5+
"tsx": true
6+
},
7+
"target": "es2022",
8+
"transform": {
9+
"useDefineForClassFields": true,
10+
"react": {
11+
"importSource": "https://esm.sh/preact",
12+
"runtime": "automatic"
13+
}
14+
},
15+
"loose": false
16+
},
17+
"module": {
18+
"type": "es6"
19+
},
20+
"minify": false,
21+
"isModule": true
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
assertType<<K>(key: K) => K>(foo);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
assertType(foo);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"jsc": {
3+
"parser": {
4+
"syntax": "typescript",
5+
"tsx": true
6+
},
7+
"target": "es2022",
8+
"transform": {
9+
"optimizer": {
10+
"globals": {
11+
"vars": {
12+
"__NODE__": "true"
13+
}
14+
},
15+
},
16+
},
17+
"loose": false,
18+
"minify": {
19+
"compress": false,
20+
"mangle": false
21+
}
22+
},
23+
"module": {
24+
"type": "es6"
25+
},
26+
"minify": false,
27+
"isModule": true
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const Context = createContext<<Key extends keyof Config>(key: Key) => Config[Key]>(
2+
(key) => properties[key]?.default
3+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
const Context = createContext((key)=>properties[key]?.default);

‎crates/swc_ecma_parser/src/macros.rs

+3
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ macro_rules! tok {
110110
('<') => {
111111
crate::token::Token::BinOp(crate::token::BinOpToken::Lt)
112112
};
113+
("<<") => {
114+
crate::token::Token::BinOp(crate::token::BinOpToken::LShift)
115+
};
113116
('>') => {
114117
crate::token::Token::BinOp(crate::token::BinOpToken::Gt)
115118
};

‎crates/swc_ecma_parser/src/parser/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1596,7 +1596,7 @@ impl<I: Tokens> Parser<I> {
15961596
let callee = self.parse_new_expr()?;
15971597
return_if_arrow!(self, callee);
15981598

1599-
let type_args = if self.input.syntax().typescript() && is!(self, '<') {
1599+
let type_args = if self.input.syntax().typescript() && is_one_of!(self, '<', "<<") {
16001600
self.try_parse_ts(|p| {
16011601
let type_args = p.parse_ts_type_args()?;
16021602
if is!(p, '(') {

‎crates/swc_ecma_parser/src/parser/input.rs

+13
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,19 @@ impl<I: Tokens> Buffer<I> {
383383
}
384384
}
385385

386+
#[inline]
387+
pub fn cut_lshift(&mut self) {
388+
debug_assert!(
389+
self.is(&tok!("<<")),
390+
"parser should only call cut_lshift when encountering LShift token"
391+
);
392+
self.cur = Some(TokenAndSpan {
393+
token: tok!('<'),
394+
span: self.cur_span().with_lo(self.cur_span().lo + BytePos(1)),
395+
had_line_break: false,
396+
});
397+
}
398+
386399
#[inline]
387400
pub fn is(&mut self, expected: &Token) -> bool {
388401
match self.cur() {

‎crates/swc_ecma_parser/src/parser/typescript.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -2666,7 +2666,11 @@ impl<I: Tokens> Parser<I> {
26662666
// Temporarily remove a JSX parsing context, which makes us scan different
26672667
// tokens.
26682668
p.ts_in_no_context(|p| {
2669-
expect!(p, '<');
2669+
if is!(p, "<<") {
2670+
p.input.cut_lshift();
2671+
} else {
2672+
expect!(p, '<');
2673+
}
26702674
p.parse_ts_delimited_list(ParsingContext::TypeParametersOrArguments, |p| {
26712675
trace_cur!(p, parse_ts_type_args__arg);
26722676

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
assertType<<K>(key: K) => K>(foo);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
{
2+
"type": "Script",
3+
"span": {
4+
"start": 1,
5+
"end": 35,
6+
"ctxt": 0
7+
},
8+
"body": [
9+
{
10+
"type": "ExpressionStatement",
11+
"span": {
12+
"start": 1,
13+
"end": 35,
14+
"ctxt": 0
15+
},
16+
"expression": {
17+
"type": "CallExpression",
18+
"span": {
19+
"start": 1,
20+
"end": 34,
21+
"ctxt": 0
22+
},
23+
"callee": {
24+
"type": "Identifier",
25+
"span": {
26+
"start": 1,
27+
"end": 11,
28+
"ctxt": 0
29+
},
30+
"value": "assertType",
31+
"optional": false
32+
},
33+
"arguments": [
34+
{
35+
"spread": null,
36+
"expression": {
37+
"type": "Identifier",
38+
"span": {
39+
"start": 30,
40+
"end": 33,
41+
"ctxt": 0
42+
},
43+
"value": "foo",
44+
"optional": false
45+
}
46+
}
47+
],
48+
"typeArguments": {
49+
"type": "TsTypeParameterInstantiation",
50+
"span": {
51+
"start": 11,
52+
"end": 29,
53+
"ctxt": 0
54+
},
55+
"params": [
56+
{
57+
"type": "TsFunctionType",
58+
"span": {
59+
"start": 12,
60+
"end": 28,
61+
"ctxt": 0
62+
},
63+
"params": [
64+
{
65+
"type": "Identifier",
66+
"span": {
67+
"start": 16,
68+
"end": 22,
69+
"ctxt": 0
70+
},
71+
"value": "key",
72+
"optional": false,
73+
"typeAnnotation": {
74+
"type": "TsTypeAnnotation",
75+
"span": {
76+
"start": 19,
77+
"end": 22,
78+
"ctxt": 0
79+
},
80+
"typeAnnotation": {
81+
"type": "TsTypeReference",
82+
"span": {
83+
"start": 21,
84+
"end": 22,
85+
"ctxt": 0
86+
},
87+
"typeName": {
88+
"type": "Identifier",
89+
"span": {
90+
"start": 21,
91+
"end": 22,
92+
"ctxt": 0
93+
},
94+
"value": "K",
95+
"optional": false
96+
},
97+
"typeParams": null
98+
}
99+
}
100+
}
101+
],
102+
"typeParams": {
103+
"type": "TsTypeParameterDeclaration",
104+
"span": {
105+
"start": 12,
106+
"end": 15,
107+
"ctxt": 0
108+
},
109+
"parameters": [
110+
{
111+
"type": "TsTypeParameter",
112+
"span": {
113+
"start": 13,
114+
"end": 14,
115+
"ctxt": 0
116+
},
117+
"name": {
118+
"type": "Identifier",
119+
"span": {
120+
"start": 13,
121+
"end": 14,
122+
"ctxt": 0
123+
},
124+
"value": "K",
125+
"optional": false
126+
},
127+
"in": false,
128+
"out": false,
129+
"const": false,
130+
"constraint": null,
131+
"default": null
132+
}
133+
]
134+
},
135+
"typeAnnotation": {
136+
"type": "TsTypeAnnotation",
137+
"span": {
138+
"start": 24,
139+
"end": 28,
140+
"ctxt": 0
141+
},
142+
"typeAnnotation": {
143+
"type": "TsTypeReference",
144+
"span": {
145+
"start": 27,
146+
"end": 28,
147+
"ctxt": 0
148+
},
149+
"typeName": {
150+
"type": "Identifier",
151+
"span": {
152+
"start": 27,
153+
"end": 28,
154+
"ctxt": 0
155+
},
156+
"value": "K",
157+
"optional": false
158+
},
159+
"typeParams": null
160+
}
161+
}
162+
}
163+
]
164+
}
165+
}
166+
}
167+
],
168+
"interpreter": null
169+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const Context = createContext<<Key extends keyof Config>(key: Key) => Config[Key]>(
2+
(key) => properties[key]?.default
3+
);

0 commit comments

Comments
 (0)
Please sign in to comment.