/src/hermes/lib/Parser/JSParserImpl-ts.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include "JSParserImpl.h" |
9 | | |
10 | | #include "hermes/AST/ESTreeJSONDumper.h" |
11 | | #include "hermes/Support/PerfSection.h" |
12 | | |
13 | | #include "llvh/Support/SaveAndRestore.h" |
14 | | |
15 | | namespace hermes { |
16 | | namespace parser { |
17 | | namespace detail { |
18 | | |
19 | | #if HERMES_PARSE_TS |
20 | | |
21 | | Optional<ESTree::Node *> JSParserImpl::parseTypeAnnotationTS( |
22 | 0 | Optional<SMLoc> wrappedStart) { |
23 | 0 | llvh::SaveAndRestore<bool> saveParam(allowAnonFunctionType_, true); |
24 | |
|
25 | 0 | SMLoc start = tok_->getStartLoc(); |
26 | 0 | ESTree::Node *result = nullptr; |
27 | |
|
28 | 0 | if (check(TokenKind::identifier)) { |
29 | | // Need to check if this is a predicate, which requires backtracking. |
30 | 0 | JSLexer::SavePoint savePoint{&lexer_}; |
31 | 0 | ESTree::Node *id = setLocation( |
32 | 0 | tok_, |
33 | 0 | tok_, |
34 | 0 | new (context_) |
35 | 0 | ESTree::IdentifierNode(tok_->getIdentifier(), nullptr, false)); |
36 | 0 | advance(JSLexer::GrammarContext::Type); |
37 | 0 | if (check(isIdent_)) { |
38 | 0 | SMLoc wrappedStart = advance(JSLexer::GrammarContext::Type).Start; |
39 | 0 | CHECK_RECURSION; |
40 | 0 | auto optType = parseTypeAnnotationTS(wrappedStart); |
41 | 0 | if (!optType) |
42 | 0 | return None; |
43 | 0 | result = setLocation( |
44 | 0 | start, |
45 | 0 | getPrevTokenEndLoc(), |
46 | 0 | new (context_) ESTree::TSTypePredicateNode(id, *optType)); |
47 | 0 | } else { |
48 | 0 | savePoint.restore(); |
49 | 0 | } |
50 | 0 | } |
51 | | |
52 | 0 | if (!result) { |
53 | 0 | if (check(TokenKind::rw_new)) { |
54 | 0 | advance(JSLexer::GrammarContext::Type); |
55 | 0 | ESTree::Node *typeParams = nullptr; |
56 | 0 | if (check(TokenKind::less)) { |
57 | 0 | auto optTypeParams = parseTSTypeParameters(); |
58 | 0 | if (!optTypeParams) |
59 | 0 | return None; |
60 | 0 | typeParams = *optTypeParams; |
61 | 0 | } |
62 | 0 | if (!need( |
63 | 0 | TokenKind::l_paren, |
64 | 0 | "in constructor type", |
65 | 0 | "start of type", |
66 | 0 | start)) |
67 | 0 | return None; |
68 | 0 | auto optResult = parseTSFunctionOrParenthesizedType( |
69 | 0 | start, typeParams, IsConstructorType::Yes); |
70 | 0 | if (!optResult) |
71 | 0 | return None; |
72 | 0 | result = *optResult; |
73 | 0 | } else if (check(TokenKind::less)) { |
74 | 0 | auto optTypeParams = parseTSTypeParameters(); |
75 | 0 | if (!optTypeParams) |
76 | 0 | return None; |
77 | 0 | if (!need(TokenKind::l_paren, "in function type", "start of type", start)) |
78 | 0 | return None; |
79 | 0 | auto optResult = parseTSFunctionOrParenthesizedType( |
80 | 0 | start, *optTypeParams, IsConstructorType::No); |
81 | 0 | if (!optResult) |
82 | 0 | return None; |
83 | 0 | result = *optResult; |
84 | 0 | } else { |
85 | 0 | auto optResult = parseTSUnionType(); |
86 | 0 | if (!optResult) |
87 | 0 | return None; |
88 | 0 | result = *optResult; |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | 0 | if (checkAndEat(TokenKind::rw_extends, JSLexer::GrammarContext::Type)) { |
93 | | // Parse a conditional type. |
94 | 0 | auto optCheck = parseTypeAnnotationTS(); |
95 | 0 | if (!optCheck) |
96 | 0 | return None; |
97 | 0 | if (!eat( |
98 | 0 | TokenKind::question, |
99 | 0 | JSLexer::GrammarContext::Type, |
100 | 0 | "in conditional type", |
101 | 0 | "start of type", |
102 | 0 | start)) |
103 | 0 | return None; |
104 | | |
105 | 0 | auto optTrue = parseTypeAnnotationTS(); |
106 | 0 | if (!optTrue) |
107 | 0 | return None; |
108 | 0 | if (!eat( |
109 | 0 | TokenKind::colon, |
110 | 0 | JSLexer::GrammarContext::Type, |
111 | 0 | "in conditional type", |
112 | 0 | "start of type", |
113 | 0 | start)) |
114 | 0 | return None; |
115 | | |
116 | 0 | auto optFalse = parseTypeAnnotationTS(); |
117 | 0 | if (!optFalse) |
118 | 0 | return None; |
119 | | |
120 | 0 | result = setLocation( |
121 | 0 | result, |
122 | 0 | getPrevTokenEndLoc(), |
123 | 0 | new (context_) ESTree::TSConditionalTypeNode( |
124 | 0 | result, *optCheck, *optTrue, *optFalse)); |
125 | 0 | } |
126 | | |
127 | 0 | if (wrappedStart) |
128 | 0 | return setLocation( |
129 | 0 | *wrappedStart, |
130 | 0 | result, |
131 | 0 | new (context_) ESTree::TSTypeAnnotationNode(result)); |
132 | | |
133 | 0 | return result; |
134 | 0 | } |
135 | | |
136 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSUnionType() { |
137 | 0 | SMLoc start = tok_->getStartLoc(); |
138 | 0 | checkAndEat(TokenKind::pipe, JSLexer::GrammarContext::Type); |
139 | |
|
140 | 0 | auto optFirst = parseTSIntersectionType(); |
141 | 0 | if (!optFirst) |
142 | 0 | return None; |
143 | | |
144 | 0 | if (!check(TokenKind::pipe)) { |
145 | | // Done with the union, move on. |
146 | 0 | return *optFirst; |
147 | 0 | } |
148 | | |
149 | 0 | ESTree::NodeList types{}; |
150 | 0 | types.push_back(**optFirst); |
151 | |
|
152 | 0 | while (checkAndEat(TokenKind::pipe, JSLexer::GrammarContext::Type)) { |
153 | 0 | auto optInt = parseTSIntersectionType(); |
154 | 0 | if (!optInt) |
155 | 0 | return None; |
156 | 0 | types.push_back(**optInt); |
157 | 0 | } |
158 | | |
159 | 0 | return setLocation( |
160 | 0 | start, |
161 | 0 | getPrevTokenEndLoc(), |
162 | 0 | new (context_) ESTree::TSUnionTypeNode(std::move(types))); |
163 | 0 | } |
164 | | |
165 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSIntersectionType() { |
166 | 0 | SMLoc start = tok_->getStartLoc(); |
167 | 0 | checkAndEat(TokenKind::amp, JSLexer::GrammarContext::Type); |
168 | |
|
169 | 0 | auto optFirst = parseTSPostfixType(); |
170 | 0 | if (!optFirst) |
171 | 0 | return None; |
172 | | |
173 | 0 | if (!check(TokenKind::amp)) { |
174 | | // Done with the union, move on. |
175 | 0 | return *optFirst; |
176 | 0 | } |
177 | | |
178 | 0 | ESTree::NodeList types{}; |
179 | 0 | types.push_back(**optFirst); |
180 | |
|
181 | 0 | while (checkAndEat(TokenKind::amp, JSLexer::GrammarContext::Type)) { |
182 | 0 | auto optInt = parseTSPostfixType(); |
183 | 0 | if (!optInt) |
184 | 0 | return None; |
185 | 0 | types.push_back(**optInt); |
186 | 0 | } |
187 | | |
188 | 0 | return setLocation( |
189 | 0 | start, |
190 | 0 | getPrevTokenEndLoc(), |
191 | 0 | new (context_) ESTree::TSIntersectionTypeNode(std::move(types))); |
192 | 0 | } |
193 | | |
194 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSTupleType() { |
195 | 0 | assert(check(TokenKind::l_square)); |
196 | 0 | SMLoc start = advance(JSLexer::GrammarContext::Type).Start; |
197 | |
|
198 | 0 | ESTree::NodeList types{}; |
199 | |
|
200 | 0 | while (!check(TokenKind::r_square)) { |
201 | 0 | auto optType = parseTypeAnnotationTS(); |
202 | 0 | if (!optType) |
203 | 0 | return None; |
204 | 0 | types.push_back(**optType); |
205 | |
|
206 | 0 | if (!checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) |
207 | 0 | break; |
208 | 0 | } |
209 | | |
210 | 0 | if (!need( |
211 | 0 | TokenKind::r_square, |
212 | 0 | "at end of tuple type annotation", |
213 | 0 | "start of tuple", |
214 | 0 | start)) |
215 | 0 | return None; |
216 | | |
217 | 0 | return setLocation( |
218 | 0 | start, |
219 | 0 | advance(JSLexer::GrammarContext::Type).End, |
220 | 0 | new (context_) ESTree::TSTupleTypeNode(std::move(types))); |
221 | 0 | } |
222 | | |
223 | | Optional<ESTree::Node *> JSParserImpl::parseTSFunctionOrParenthesizedType( |
224 | | SMLoc start, |
225 | | ESTree::Node *typeParams, |
226 | 0 | IsConstructorType isConstructorType) { |
227 | 0 | assert(check(TokenKind::l_paren)); |
228 | | // This is either |
229 | | // ( Type ) |
230 | | // ^ |
231 | | // or |
232 | | // ( ParamList ) => Type |
233 | | // ^ |
234 | | // so we use a similar approach to arrow function parameters by keeping track |
235 | | // and reparsing in certain cases. |
236 | 0 | advance(JSLexer::GrammarContext::Type); |
237 | |
|
238 | 0 | bool isFunction = typeParams != nullptr; |
239 | 0 | bool hasRest = false; |
240 | 0 | ESTree::Node *type = nullptr; |
241 | 0 | ESTree::NodeList params{}; |
242 | |
|
243 | 0 | if (check(TokenKind::rw_this)) { |
244 | 0 | OptValue<TokenKind> optNext = lexer_.lookahead1(None); |
245 | 0 | if (optNext.hasValue() && *optNext == TokenKind::colon) { |
246 | 0 | SMLoc thisStart = advance(JSLexer::GrammarContext::Type).Start; |
247 | 0 | advance(JSLexer::GrammarContext::Type); |
248 | 0 | CHECK_RECURSION; |
249 | 0 | auto typeAnnotation = parseTypeAnnotationTS(); |
250 | 0 | if (!typeAnnotation) |
251 | 0 | return None; |
252 | | |
253 | 0 | params.push_back(*setLocation( |
254 | 0 | thisStart, |
255 | 0 | getPrevTokenEndLoc(), |
256 | 0 | new (context_) ESTree::IdentifierNode( |
257 | 0 | thisIdent_, *typeAnnotation, /* optional */ false))); |
258 | 0 | checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type); |
259 | 0 | } else if (optNext.hasValue() && *optNext == TokenKind::question) { |
260 | 0 | error(tok_->getSourceRange(), "'this' constraint may not be optional"); |
261 | 0 | return None; |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | 0 | if (allowAnonFunctionType_ && |
266 | 0 | checkAndEat(TokenKind::dotdotdot, JSLexer::GrammarContext::Type)) { |
267 | 0 | isFunction = true; |
268 | 0 | hasRest = true; |
269 | | // Must be parameters, and this must be the last one. |
270 | 0 | auto optName = parseTSFunctionTypeParam(); |
271 | 0 | if (!optName) |
272 | 0 | return None; |
273 | 0 | params.push_back(*setLocation( |
274 | 0 | start, |
275 | 0 | getPrevTokenEndLoc(), |
276 | 0 | new (context_) ESTree::RestElementNode(*optName))); |
277 | 0 | } else if (check(TokenKind::l_paren)) { |
278 | 0 | auto optType = parseTypeAnnotationTS(); |
279 | 0 | if (!optType) |
280 | 0 | return None; |
281 | 0 | type = *optType; |
282 | 0 | } else if (check(TokenKind::r_paren)) { |
283 | 0 | isFunction = true; |
284 | | // ( ) |
285 | | // ^ |
286 | | // No parameters, but this must be an empty param list. |
287 | 0 | } else { |
288 | | // Not sure yet whether this is a param or simply a type. |
289 | 0 | auto optParam = parseTSFunctionTypeParam(); |
290 | 0 | if (!optParam) |
291 | 0 | return None; |
292 | 0 | if (auto *param = |
293 | 0 | llvh::dyn_cast<ESTree::TSParameterPropertyNode>(*optParam)) { |
294 | 0 | if (param && |
295 | 0 | (param->_accessibility || param->_export || param->_readonly || |
296 | 0 | param->_static)) { |
297 | | // Must be a param. |
298 | 0 | isFunction = true; |
299 | 0 | } |
300 | 0 | params.push_back(*param); |
301 | 0 | } else if ( |
302 | 0 | auto *ident = llvh::dyn_cast<ESTree::IdentifierNode>(*optParam)) { |
303 | 0 | params.push_back(*ident); |
304 | 0 | type = ident->_typeAnnotation |
305 | 0 | ? ident->_typeAnnotation |
306 | 0 | : reparseIdentifierAsTSTypeAnnotation(ident); |
307 | 0 | if (ident->_typeAnnotation || ident->_optional) { |
308 | | // Must be a param. |
309 | 0 | isFunction = true; |
310 | 0 | } |
311 | 0 | } else { |
312 | 0 | type = *optParam; |
313 | 0 | params.push_back(**optParam); |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | | // If isFunction was already forced by something previously then we |
318 | | // have no choice but to attempt to parse as a function type annotation. |
319 | 0 | if ((isFunction || allowAnonFunctionType_) && |
320 | 0 | checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) { |
321 | 0 | isFunction = true; |
322 | 0 | while (!check(TokenKind::r_paren)) { |
323 | 0 | bool isRest = !hasRest && |
324 | 0 | checkAndEat(TokenKind::dotdotdot, JSLexer::GrammarContext::Type); |
325 | |
|
326 | 0 | auto optParam = parseTSFunctionTypeParam(); |
327 | 0 | if (!optParam) |
328 | 0 | return None; |
329 | 0 | if (isRest) { |
330 | 0 | params.push_back(*setLocation( |
331 | 0 | start, |
332 | 0 | getPrevTokenEndLoc(), |
333 | 0 | new (context_) ESTree::RestElementNode(*optParam))); |
334 | 0 | checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type); |
335 | 0 | break; |
336 | 0 | } else { |
337 | 0 | params.push_back(**optParam); |
338 | 0 | } |
339 | | |
340 | 0 | if (!checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) |
341 | 0 | break; |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | 0 | if (!eat( |
346 | 0 | TokenKind::r_paren, |
347 | 0 | JSLexer::GrammarContext::Type, |
348 | 0 | "at end of function type parameters", |
349 | 0 | "start of parameters", |
350 | 0 | start)) |
351 | 0 | return None; |
352 | | |
353 | 0 | if (isFunction) { |
354 | 0 | if (!eat( |
355 | 0 | TokenKind::equalgreater, |
356 | 0 | JSLexer::GrammarContext::Type, |
357 | 0 | "in function type", |
358 | 0 | "start of function", |
359 | 0 | start)) |
360 | 0 | return None; |
361 | 0 | } else if (allowAnonFunctionType_) { |
362 | 0 | if (checkAndEat(TokenKind::equalgreater, JSLexer::GrammarContext::Type)) { |
363 | 0 | isFunction = true; |
364 | 0 | } |
365 | 0 | } |
366 | | |
367 | 0 | if (!isFunction) { |
368 | 0 | type->incParens(); |
369 | 0 | return type; |
370 | 0 | } |
371 | | |
372 | 0 | auto optReturnType = parseTypeAnnotationTS(); |
373 | 0 | if (!optReturnType) |
374 | 0 | return None; |
375 | | |
376 | 0 | if (isConstructorType == IsConstructorType::Yes) { |
377 | 0 | return setLocation( |
378 | 0 | start, |
379 | 0 | getPrevTokenEndLoc(), |
380 | 0 | new (context_) ESTree::TSConstructorTypeNode( |
381 | 0 | std::move(params), *optReturnType, typeParams)); |
382 | 0 | } |
383 | | |
384 | 0 | return setLocation( |
385 | 0 | start, |
386 | 0 | getPrevTokenEndLoc(), |
387 | 0 | new (context_) ESTree::TSFunctionTypeNode( |
388 | 0 | std::move(params), *optReturnType, typeParams)); |
389 | 0 | } |
390 | | |
391 | | bool JSParserImpl::parseTSFunctionTypeParams( |
392 | | SMLoc start, |
393 | 0 | ESTree::NodeList ¶ms) { |
394 | 0 | assert(check(TokenKind::l_paren)); |
395 | | |
396 | 0 | advance(JSLexer::GrammarContext::Type); |
397 | |
|
398 | 0 | while (!check(TokenKind::r_paren)) { |
399 | 0 | auto optParam = parseTSFunctionTypeParam(); |
400 | 0 | if (!optParam) |
401 | 0 | return false; |
402 | 0 | params.push_back(**optParam); |
403 | |
|
404 | 0 | if (!checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) |
405 | 0 | break; |
406 | 0 | } |
407 | | |
408 | 0 | if (!eat( |
409 | 0 | TokenKind::r_paren, |
410 | 0 | JSLexer::GrammarContext::Type, |
411 | 0 | "at end of function type parameters", |
412 | 0 | "start of parameters", |
413 | 0 | start)) |
414 | 0 | return false; |
415 | | |
416 | 0 | return true; |
417 | 0 | } |
418 | | |
419 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSFunctionTypeParam() { |
420 | 0 | SMLoc start = tok_->getStartLoc(); |
421 | |
|
422 | 0 | ESTree::NodeLabel accessibilityNode = nullptr; |
423 | 0 | bool readonlyNode = false; |
424 | 0 | bool staticNode = false; |
425 | 0 | bool exportNode = false; |
426 | |
|
427 | 0 | while (checkN( |
428 | 0 | TokenKind::identifier, TokenKind::rw_static, TokenKind::rw_export)) { |
429 | | // Check if this is a modifier. |
430 | 0 | if (!staticNode && checkN(TokenKind::rw_static, staticIdent_)) { |
431 | 0 | advance(JSLexer::GrammarContext::Type); |
432 | 0 | if (checkN( |
433 | 0 | TokenKind::identifier, |
434 | 0 | TokenKind::rw_static, |
435 | 0 | TokenKind::rw_export)) { |
436 | 0 | staticNode = true; |
437 | 0 | continue; |
438 | 0 | } |
439 | 0 | } |
440 | 0 | if (!exportNode && checkN(TokenKind::rw_export)) { |
441 | 0 | advance(JSLexer::GrammarContext::Type); |
442 | 0 | if (checkN( |
443 | 0 | TokenKind::identifier, |
444 | 0 | TokenKind::rw_static, |
445 | 0 | TokenKind::rw_export)) { |
446 | 0 | exportNode = true; |
447 | 0 | continue; |
448 | 0 | } |
449 | 0 | } |
450 | 0 | if (!readonlyNode && checkN(readonlyIdent_)) { |
451 | 0 | advance(JSLexer::GrammarContext::Type); |
452 | 0 | if (checkN( |
453 | 0 | TokenKind::identifier, |
454 | 0 | TokenKind::rw_static, |
455 | 0 | TokenKind::rw_export)) { |
456 | 0 | readonlyNode = true; |
457 | 0 | continue; |
458 | 0 | } |
459 | 0 | } |
460 | 0 | if (!accessibilityNode) { |
461 | 0 | if (checkN(TokenKind::rw_public, publicIdent_)) { |
462 | 0 | advance(JSLexer::GrammarContext::Type); |
463 | 0 | if (checkN( |
464 | 0 | TokenKind::identifier, |
465 | 0 | TokenKind::rw_static, |
466 | 0 | TokenKind::rw_export)) { |
467 | 0 | accessibilityNode = publicIdent_; |
468 | 0 | continue; |
469 | 0 | } |
470 | 0 | } |
471 | 0 | if (checkN(TokenKind::rw_private, privateIdent_)) { |
472 | 0 | advance(JSLexer::GrammarContext::Type); |
473 | 0 | if (checkN( |
474 | 0 | TokenKind::identifier, |
475 | 0 | TokenKind::rw_static, |
476 | 0 | TokenKind::rw_export)) { |
477 | 0 | accessibilityNode = privateIdent_; |
478 | 0 | continue; |
479 | 0 | } |
480 | 0 | } |
481 | 0 | if (checkN(TokenKind::rw_protected, protectedIdent_)) { |
482 | 0 | advance(JSLexer::GrammarContext::Type); |
483 | 0 | if (checkN( |
484 | 0 | TokenKind::identifier, |
485 | 0 | TokenKind::rw_static, |
486 | 0 | TokenKind::rw_export)) { |
487 | 0 | accessibilityNode = protectedIdent_; |
488 | 0 | continue; |
489 | 0 | } |
490 | 0 | } |
491 | 0 | } |
492 | | |
493 | | // Not a modifier. |
494 | 0 | break; |
495 | 0 | } |
496 | |
|
497 | 0 | auto optParam = parseBindingElement(Param{}); |
498 | 0 | if (!optParam) |
499 | 0 | return None; |
500 | | |
501 | 0 | if (accessibilityNode || readonlyNode || staticNode || exportNode) { |
502 | 0 | return setLocation( |
503 | 0 | start, |
504 | 0 | getPrevTokenEndLoc(), |
505 | 0 | new (context_) ESTree::TSParameterPropertyNode( |
506 | 0 | *optParam, |
507 | 0 | accessibilityNode, |
508 | 0 | readonlyNode, |
509 | 0 | staticNode, |
510 | 0 | exportNode)); |
511 | 0 | } |
512 | | |
513 | 0 | return *optParam; |
514 | 0 | } |
515 | | |
516 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSDeclaration() { |
517 | 0 | assert(checkDeclaration()); |
518 | | |
519 | 0 | SMLoc start = tok_->getStartLoc(); |
520 | |
|
521 | 0 | if (checkN(TokenKind::rw_interface, interfaceIdent_)) { |
522 | 0 | return parseTSInterfaceDeclaration(); |
523 | 0 | } |
524 | | |
525 | 0 | if (checkAndEat(typeIdent_, JSLexer::GrammarContext::Type)) { |
526 | 0 | return parseTSTypeAliasDeclaration(start); |
527 | 0 | } |
528 | | |
529 | 0 | if (check(namespaceIdent_)) { |
530 | 0 | return parseTSNamespaceDeclaration(); |
531 | 0 | } |
532 | | |
533 | 0 | assert(check(TokenKind::rw_enum)); |
534 | 0 | return parseTSEnumDeclaration(); |
535 | 0 | } |
536 | | |
537 | | Optional<ESTree::Node *> JSParserImpl::parseTSTypeAliasDeclaration( |
538 | 0 | SMLoc start) { |
539 | 0 | if (!need( |
540 | 0 | TokenKind::identifier, "in type alias", "start of type alias", start)) |
541 | 0 | return None; |
542 | | |
543 | 0 | ESTree::Node *id = setLocation( |
544 | 0 | tok_, |
545 | 0 | tok_, |
546 | 0 | new (context_) |
547 | 0 | ESTree::IdentifierNode(tok_->getIdentifier(), nullptr, false)); |
548 | 0 | advance(JSLexer::GrammarContext::Type); |
549 | |
|
550 | 0 | ESTree::Node *typeParams = nullptr; |
551 | 0 | if (check(TokenKind::less)) { |
552 | 0 | auto optTypeParams = parseTSTypeParameters(); |
553 | 0 | if (!optTypeParams) |
554 | 0 | return None; |
555 | 0 | typeParams = *optTypeParams; |
556 | 0 | } |
557 | | |
558 | 0 | if (!eat( |
559 | 0 | TokenKind::equal, |
560 | 0 | JSLexer::GrammarContext::Type, |
561 | 0 | "in type alias", |
562 | 0 | "start of type alias", |
563 | 0 | start)) |
564 | 0 | return None; |
565 | | |
566 | 0 | auto optRight = parseTypeAnnotationTS(); |
567 | 0 | if (!optRight) |
568 | 0 | return None; |
569 | 0 | ESTree::Node *right = *optRight; |
570 | |
|
571 | 0 | if (!eatSemi(true)) |
572 | 0 | return None; |
573 | | |
574 | 0 | return setLocation( |
575 | 0 | start, |
576 | 0 | getPrevTokenEndLoc(), |
577 | 0 | new (context_) ESTree::TSTypeAliasDeclarationNode(id, typeParams, right)); |
578 | 0 | } |
579 | | |
580 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSInterfaceDeclaration() { |
581 | 0 | assert(checkN(TokenKind::rw_interface, interfaceIdent_)); |
582 | 0 | SMLoc start = advance(JSLexer::GrammarContext::Type).Start; |
583 | |
|
584 | 0 | if (!check(TokenKind::identifier) && !tok_->isResWord()) { |
585 | 0 | errorExpected( |
586 | 0 | TokenKind::identifier, |
587 | 0 | "in interface declaration", |
588 | 0 | "start of interface", |
589 | 0 | start); |
590 | 0 | return None; |
591 | 0 | } |
592 | | |
593 | 0 | ESTree::Node *id = setLocation( |
594 | 0 | tok_, |
595 | 0 | tok_, |
596 | 0 | new (context_) ESTree::IdentifierNode( |
597 | 0 | tok_->getResWordOrIdentifier(), nullptr, false)); |
598 | 0 | advance(JSLexer::GrammarContext::Type); |
599 | |
|
600 | 0 | ESTree::Node *typeParams = nullptr; |
601 | 0 | if (check(TokenKind::less)) { |
602 | 0 | auto optTypeParams = parseTSTypeParameters(); |
603 | 0 | if (!optTypeParams) |
604 | 0 | return None; |
605 | 0 | typeParams = *optTypeParams; |
606 | 0 | } |
607 | | |
608 | 0 | ESTree::NodeList extends{}; |
609 | 0 | if (checkAndEat( |
610 | 0 | TokenKind::rw_extends, JSLexer::GrammarContext::AllowRegExp)) { |
611 | 0 | do { |
612 | 0 | auto optExpr = parseTSTypeReference(); |
613 | 0 | if (!optExpr) |
614 | 0 | return None; |
615 | 0 | ESTree::TSTypeReferenceNode *expr = *optExpr; |
616 | |
|
617 | 0 | ESTree::Node *typeArgs = nullptr; |
618 | 0 | if (expr->_typeParameters) { |
619 | 0 | typeArgs = expr->_typeParameters; |
620 | 0 | expr->_typeParameters = nullptr; |
621 | 0 | } |
622 | |
|
623 | 0 | extends.push_back(*setLocation( |
624 | 0 | start, |
625 | 0 | getPrevTokenEndLoc(), |
626 | 0 | new (context_) ESTree::TSInterfaceHeritageNode(expr, typeArgs))); |
627 | |
|
628 | 0 | if (!checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) |
629 | 0 | break; |
630 | 0 | } while (!check(TokenKind::l_brace)); |
631 | 0 | } |
632 | | |
633 | 0 | SMLoc bodyStart = tok_->getStartLoc(); |
634 | |
|
635 | 0 | if (!eat( |
636 | 0 | TokenKind::l_brace, |
637 | 0 | JSLexer::GrammarContext::Type, |
638 | 0 | "in interface declaration", |
639 | 0 | "start of interface", |
640 | 0 | start)) |
641 | 0 | return None; |
642 | | |
643 | 0 | ESTree::NodeList members{}; |
644 | |
|
645 | 0 | while (!check(TokenKind::r_brace)) { |
646 | 0 | auto optMember = parseTSObjectTypeMember(); |
647 | 0 | if (!optMember) |
648 | 0 | return None; |
649 | 0 | members.push_back(**optMember); |
650 | |
|
651 | 0 | bool hasNext = check(TokenKind::comma, TokenKind::semi); |
652 | 0 | if (hasNext) { |
653 | 0 | advance(JSLexer::GrammarContext::Type); |
654 | 0 | } else { |
655 | 0 | break; |
656 | 0 | } |
657 | 0 | } |
658 | | |
659 | 0 | if (!eat( |
660 | 0 | TokenKind::r_brace, |
661 | 0 | JSLexer::GrammarContext::Type, |
662 | 0 | "at end of object type", |
663 | 0 | "start of object type", |
664 | 0 | start)) |
665 | 0 | return None; |
666 | | |
667 | 0 | ESTree::Node *body = setLocation( |
668 | 0 | bodyStart, |
669 | 0 | getPrevTokenEndLoc(), |
670 | 0 | new (context_) ESTree::TSInterfaceBodyNode(std::move(members))); |
671 | |
|
672 | 0 | return setLocation( |
673 | 0 | start, |
674 | 0 | getPrevTokenEndLoc(), |
675 | 0 | new (context_) ESTree::TSInterfaceDeclarationNode( |
676 | 0 | id, body, std::move(extends), typeParams)); |
677 | 0 | } |
678 | | |
679 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSEnumDeclaration() { |
680 | 0 | assert(check(TokenKind::rw_enum)); |
681 | 0 | SMLoc start = advance(JSLexer::GrammarContext::Type).Start; |
682 | |
|
683 | 0 | auto optName = parseBindingIdentifier(Param{}); |
684 | 0 | if (!optName) { |
685 | 0 | errorExpected( |
686 | 0 | TokenKind::identifier, "in enum declaration", "start of enum", start); |
687 | 0 | return None; |
688 | 0 | } |
689 | 0 | ESTree::Node *name = *optName; |
690 | |
|
691 | 0 | if (!eat( |
692 | 0 | TokenKind::l_brace, |
693 | 0 | JSLexer::GrammarContext::Type, |
694 | 0 | "in enum declaration", |
695 | 0 | "start of enum", |
696 | 0 | start)) |
697 | 0 | return None; |
698 | | |
699 | 0 | ESTree::NodeList members{}; |
700 | |
|
701 | 0 | while (!check(TokenKind::r_brace)) { |
702 | 0 | auto optMember = parseTSEnumMember(); |
703 | 0 | if (!optMember) |
704 | 0 | return None; |
705 | 0 | members.push_back(**optMember); |
706 | |
|
707 | 0 | if (!checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) |
708 | 0 | break; |
709 | 0 | } |
710 | | |
711 | 0 | if (!eat( |
712 | 0 | TokenKind::r_brace, |
713 | 0 | JSLexer::GrammarContext::Type, |
714 | 0 | "in enum declaration", |
715 | 0 | "start of enum", |
716 | 0 | start)) |
717 | 0 | return None; |
718 | | |
719 | 0 | return setLocation( |
720 | 0 | start, |
721 | 0 | getPrevTokenEndLoc(), |
722 | 0 | new (context_) ESTree::TSEnumDeclarationNode(name, std::move(members))); |
723 | 0 | } |
724 | | |
725 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSEnumMember() { |
726 | 0 | SMLoc start = tok_->getStartLoc(); |
727 | |
|
728 | 0 | auto optName = parseBindingIdentifier(Param{}); |
729 | 0 | if (!optName) { |
730 | 0 | errorExpected( |
731 | 0 | TokenKind::identifier, "in enum member", "start of member", start); |
732 | 0 | return None; |
733 | 0 | } |
734 | 0 | ESTree::Node *name = *optName; |
735 | |
|
736 | 0 | ESTree::Node *init = nullptr; |
737 | 0 | if (checkAndEat(TokenKind::equal)) { |
738 | 0 | auto optExpr = parseAssignmentExpression(); |
739 | 0 | if (!optExpr) |
740 | 0 | return None; |
741 | 0 | init = *optExpr; |
742 | 0 | } |
743 | | |
744 | 0 | return setLocation( |
745 | 0 | start, |
746 | 0 | getPrevTokenEndLoc(), |
747 | 0 | new (context_) ESTree::TSEnumMemberNode(name, init)); |
748 | 0 | } |
749 | | |
750 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSNamespaceDeclaration() { |
751 | 0 | assert(check(namespaceIdent_)); |
752 | 0 | SMLoc start = advance(JSLexer::GrammarContext::Type).Start; |
753 | |
|
754 | 0 | auto optName = parseTSQualifiedName(); |
755 | 0 | if (!optName) { |
756 | 0 | errorExpected( |
757 | 0 | TokenKind::identifier, |
758 | 0 | "in namespace declaration", |
759 | 0 | "start of namespace", |
760 | 0 | start); |
761 | 0 | return None; |
762 | 0 | } |
763 | 0 | ESTree::Node *name = *optName; |
764 | |
|
765 | 0 | if (!eat( |
766 | 0 | TokenKind::l_brace, |
767 | 0 | JSLexer::GrammarContext::Type, |
768 | 0 | "in namespace declaration", |
769 | 0 | "start of namespace", |
770 | 0 | start)) |
771 | 0 | return None; |
772 | | |
773 | 0 | ESTree::NodeList members{}; |
774 | |
|
775 | 0 | while (!check(TokenKind::r_brace)) { |
776 | 0 | auto optMember = |
777 | 0 | parseStatementListItem(Param{}, AllowImportExport::Yes, members); |
778 | 0 | if (!optMember) |
779 | 0 | return None; |
780 | 0 | } |
781 | | |
782 | 0 | if (!eat( |
783 | 0 | TokenKind::r_brace, |
784 | 0 | JSLexer::GrammarContext::Type, |
785 | 0 | "in namespace declaration", |
786 | 0 | "start of namespace", |
787 | 0 | start)) |
788 | 0 | return None; |
789 | | |
790 | 0 | ESTree::Node *body = setLocation( |
791 | 0 | start, |
792 | 0 | getPrevTokenEndLoc(), |
793 | 0 | new (context_) ESTree::TSModuleBlockNode(std::move(members))); |
794 | |
|
795 | 0 | return setLocation( |
796 | 0 | start, |
797 | 0 | getPrevTokenEndLoc(), |
798 | 0 | new (context_) ESTree::TSModuleMemberNode(name, body)); |
799 | 0 | } |
800 | | |
801 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSTypeParameters() { |
802 | 0 | assert(check(TokenKind::less)); |
803 | 0 | SMLoc start = advance(JSLexer::GrammarContext::Type).Start; |
804 | |
|
805 | 0 | ESTree::NodeList params{}; |
806 | |
|
807 | 0 | do { |
808 | 0 | auto optType = parseTSTypeParameter(); |
809 | 0 | if (!optType) |
810 | 0 | return None; |
811 | 0 | params.push_back(**optType); |
812 | |
|
813 | 0 | if (!checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) |
814 | 0 | break; |
815 | 0 | } while (!check(TokenKind::greater)); |
816 | | |
817 | 0 | SMLoc end = tok_->getEndLoc(); |
818 | 0 | if (!eat( |
819 | 0 | TokenKind::greater, |
820 | 0 | JSLexer::GrammarContext::Type, |
821 | 0 | "at end of type parameters", |
822 | 0 | "start of type parameters", |
823 | 0 | start)) |
824 | 0 | return None; |
825 | | |
826 | 0 | return setLocation( |
827 | 0 | start, |
828 | 0 | end, |
829 | 0 | new (context_) ESTree::TSTypeParameterDeclarationNode(std::move(params))); |
830 | 0 | } |
831 | | |
832 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSTypeParameter() { |
833 | 0 | SMLoc start = tok_->getStartLoc(); |
834 | |
|
835 | 0 | if (!need(TokenKind::identifier, "in type parameter", nullptr, {})) |
836 | 0 | return None; |
837 | 0 | ESTree::IdentifierNode *name = setLocation( |
838 | 0 | tok_, |
839 | 0 | tok_, |
840 | 0 | new (context_) |
841 | 0 | ESTree::IdentifierNode(tok_->getIdentifier(), nullptr, false)); |
842 | 0 | advance(JSLexer::GrammarContext::Type); |
843 | |
|
844 | 0 | ESTree::Node *constraint = nullptr; |
845 | 0 | if (checkAndEat(TokenKind::rw_extends, JSLexer::GrammarContext::Type)) { |
846 | 0 | auto optType = parseTypeAnnotationTS(); |
847 | 0 | if (!optType) |
848 | 0 | return None; |
849 | 0 | constraint = *optType; |
850 | 0 | } |
851 | | |
852 | 0 | ESTree::Node *init = nullptr; |
853 | 0 | if (checkAndEat(TokenKind::equal, JSLexer::GrammarContext::Type)) { |
854 | 0 | auto optType = parseTypeAnnotationTS(); |
855 | 0 | if (!optType) |
856 | 0 | return None; |
857 | 0 | init = *optType; |
858 | 0 | } |
859 | | |
860 | 0 | return setLocation( |
861 | 0 | start, |
862 | 0 | getPrevTokenEndLoc(), |
863 | 0 | new (context_) ESTree::TSTypeParameterNode(name, constraint, init)); |
864 | 0 | } |
865 | | |
866 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSPostfixType() { |
867 | 0 | SMLoc start = tok_->getStartLoc(); |
868 | 0 | auto optPrimary = parseTSPrimaryType(); |
869 | 0 | if (!optPrimary) |
870 | 0 | return None; |
871 | | |
872 | 0 | ESTree::Node *result = *optPrimary; |
873 | | |
874 | | // Parse any [] after the primary type. |
875 | 0 | while (!lexer_.isNewLineBeforeCurrentToken() && |
876 | 0 | checkAndEat(TokenKind::l_square, JSLexer::GrammarContext::Type)) { |
877 | 0 | if (check(TokenKind::r_square)) { |
878 | 0 | result = setLocation( |
879 | 0 | start, |
880 | 0 | advance(JSLexer::GrammarContext::Type).End, |
881 | 0 | new (context_) ESTree::TSArrayTypeNode(result)); |
882 | 0 | } else { |
883 | 0 | auto optType = parseTypeAnnotationTS(); |
884 | 0 | if (!optType) |
885 | 0 | return None; |
886 | 0 | if (!eat( |
887 | 0 | TokenKind::r_square, |
888 | 0 | JSLexer::GrammarContext::Type, |
889 | 0 | "in indexed access type", |
890 | 0 | "start of type", |
891 | 0 | start)) |
892 | 0 | return None; |
893 | 0 | result = setLocation( |
894 | 0 | start, |
895 | 0 | getPrevTokenEndLoc(), |
896 | 0 | new (context_) ESTree::TSIndexedAccessTypeNode(result, *optType)); |
897 | 0 | } |
898 | 0 | } |
899 | | |
900 | 0 | return result; |
901 | 0 | } |
902 | | |
903 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSPrimaryType() { |
904 | 0 | CHECK_RECURSION; |
905 | 0 | SMLoc start = tok_->getStartLoc(); |
906 | 0 | switch (tok_->getKind()) { |
907 | 0 | case TokenKind::star: |
908 | 0 | return setLocation( |
909 | 0 | start, |
910 | 0 | advance(JSLexer::GrammarContext::Type).End, |
911 | 0 | new (context_) ESTree::ExistsTypeAnnotationNode()); |
912 | 0 | case TokenKind::l_paren: |
913 | 0 | return parseTSFunctionOrParenthesizedType( |
914 | 0 | start, nullptr, IsConstructorType::No); |
915 | 0 | case TokenKind::l_brace: |
916 | 0 | return parseTSObjectType(); |
917 | 0 | case TokenKind::rw_interface: |
918 | 0 | return parseTSInterfaceDeclaration(); |
919 | 0 | case TokenKind::rw_typeof: |
920 | 0 | return parseTSTypeQuery(); |
921 | 0 | case TokenKind::l_square: |
922 | 0 | return parseTSTupleType(); |
923 | 0 | case TokenKind::rw_this: |
924 | 0 | return setLocation( |
925 | 0 | start, |
926 | 0 | advance(JSLexer::GrammarContext::Type).End, |
927 | 0 | new (context_) ESTree::TSThisTypeNode()); |
928 | 0 | case TokenKind::rw_static: |
929 | 0 | case TokenKind::identifier: |
930 | 0 | if (tok_->getResWordOrIdentifier() == anyIdent_) { |
931 | 0 | return setLocation( |
932 | 0 | start, |
933 | 0 | advance(JSLexer::GrammarContext::Type).End, |
934 | 0 | new (context_) ESTree::TSAnyKeywordNode()); |
935 | 0 | } |
936 | 0 | if (tok_->getResWordOrIdentifier() == booleanIdent_) { |
937 | 0 | return setLocation( |
938 | 0 | start, |
939 | 0 | advance(JSLexer::GrammarContext::Type).End, |
940 | 0 | new (context_) ESTree::TSBooleanKeywordNode()); |
941 | 0 | } |
942 | 0 | if (tok_->getResWordOrIdentifier() == numberIdent_) { |
943 | 0 | return setLocation( |
944 | 0 | start, |
945 | 0 | advance(JSLexer::GrammarContext::Type).End, |
946 | 0 | new (context_) ESTree::TSNumberKeywordNode()); |
947 | 0 | } |
948 | 0 | if (tok_->getResWordOrIdentifier() == symbolIdent_) { |
949 | 0 | return setLocation( |
950 | 0 | start, |
951 | 0 | advance(JSLexer::GrammarContext::Type).End, |
952 | 0 | new (context_) ESTree::TSSymbolKeywordNode()); |
953 | 0 | } |
954 | 0 | if (tok_->getResWordOrIdentifier() == stringIdent_) { |
955 | 0 | return setLocation( |
956 | 0 | start, |
957 | 0 | advance(JSLexer::GrammarContext::Type).End, |
958 | 0 | new (context_) ESTree::TSStringKeywordNode()); |
959 | 0 | } |
960 | 0 | if (tok_->getResWordOrIdentifier() == bigintIdent_) { |
961 | 0 | return setLocation( |
962 | 0 | start, |
963 | 0 | advance(JSLexer::GrammarContext::Type).End, |
964 | 0 | new (context_) ESTree::TSBigIntKeywordNode()); |
965 | 0 | } |
966 | 0 | if (tok_->getResWordOrIdentifier() == neverIdent_) { |
967 | 0 | return setLocation( |
968 | 0 | start, |
969 | 0 | advance(JSLexer::GrammarContext::Type).End, |
970 | 0 | new (context_) ESTree::TSNeverKeywordNode()); |
971 | 0 | } |
972 | 0 | if (tok_->getResWordOrIdentifier() == undefinedIdent_) { |
973 | 0 | return setLocation( |
974 | 0 | start, |
975 | 0 | advance(JSLexer::GrammarContext::Type).End, |
976 | 0 | new (context_) ESTree::TSUndefinedKeywordNode()); |
977 | 0 | } |
978 | 0 | if (tok_->getResWordOrIdentifier() == unknownIdent_) { |
979 | 0 | return setLocation( |
980 | 0 | start, |
981 | 0 | advance(JSLexer::GrammarContext::Type).End, |
982 | 0 | new (context_) ESTree::TSUnknownKeywordNode()); |
983 | 0 | } |
984 | | |
985 | 0 | { |
986 | 0 | auto optRef = parseTSTypeReference(); |
987 | 0 | if (!optRef) |
988 | 0 | return None; |
989 | 0 | return *optRef; |
990 | 0 | } |
991 | | |
992 | 0 | case TokenKind::rw_null: { |
993 | 0 | SMLoc end = advance(JSLexer::GrammarContext::Type).End; |
994 | 0 | return setLocation( |
995 | 0 | start, |
996 | 0 | end, |
997 | 0 | new (context_) ESTree::TSLiteralTypeNode(setLocation( |
998 | 0 | start, end, new (context_) ESTree::NullLiteralNode()))); |
999 | 0 | } |
1000 | | |
1001 | 0 | case TokenKind::rw_void: |
1002 | 0 | return setLocation( |
1003 | 0 | start, |
1004 | 0 | advance(JSLexer::GrammarContext::Type).End, |
1005 | 0 | new (context_) ESTree::TSVoidKeywordNode()); |
1006 | | |
1007 | 0 | case TokenKind::string_literal: { |
1008 | 0 | UniqueString *str = tok_->getStringLiteral(); |
1009 | 0 | SMLoc end = advance(JSLexer::GrammarContext::Type).End; |
1010 | 0 | return setLocation( |
1011 | 0 | start, |
1012 | 0 | end, |
1013 | 0 | new (context_) ESTree::TSLiteralTypeNode(setLocation( |
1014 | 0 | start, end, new (context_) ESTree::StringLiteralNode(str)))); |
1015 | 0 | } |
1016 | | |
1017 | 0 | case TokenKind::numeric_literal: { |
1018 | 0 | double str = tok_->getNumericLiteral(); |
1019 | 0 | SMLoc end = advance(JSLexer::GrammarContext::Type).End; |
1020 | 0 | return setLocation( |
1021 | 0 | start, |
1022 | 0 | end, |
1023 | 0 | new (context_) ESTree::TSLiteralTypeNode(setLocation( |
1024 | 0 | start, end, new (context_) ESTree::NumericLiteralNode(str)))); |
1025 | 0 | } |
1026 | | |
1027 | 0 | case TokenKind::bigint_literal: { |
1028 | 0 | UniqueString *raw = tok_->getBigIntLiteral(); |
1029 | 0 | SMLoc end = advance(JSLexer::GrammarContext::Type).End; |
1030 | 0 | return setLocation( |
1031 | 0 | start, |
1032 | 0 | end, |
1033 | 0 | new (context_) ESTree::TSLiteralTypeNode(setLocation( |
1034 | 0 | start, end, new (context_) ESTree::BigIntLiteralNode(raw)))); |
1035 | 0 | } |
1036 | | |
1037 | 0 | case TokenKind::rw_true: |
1038 | 0 | case TokenKind::rw_false: { |
1039 | 0 | bool value = check(TokenKind::rw_true); |
1040 | 0 | SMLoc end = advance(JSLexer::GrammarContext::Type).End; |
1041 | 0 | return setLocation( |
1042 | 0 | start, |
1043 | 0 | end, |
1044 | 0 | new (context_) ESTree::TSLiteralTypeNode(setLocation( |
1045 | 0 | start, end, new (context_) ESTree::BooleanLiteralNode(value)))); |
1046 | 0 | } |
1047 | | |
1048 | 0 | default: |
1049 | 0 | if (tok_->isResWord()) { |
1050 | 0 | auto optRef = parseTSTypeReference(); |
1051 | 0 | if (!optRef) |
1052 | 0 | return None; |
1053 | 0 | return *optRef; |
1054 | 0 | } |
1055 | 0 | error(tok_->getStartLoc(), "unexpected token in type annotation"); |
1056 | 0 | return None; |
1057 | 0 | } |
1058 | 0 | } |
1059 | | |
1060 | 0 | Optional<ESTree::TSTypeReferenceNode *> JSParserImpl::parseTSTypeReference() { |
1061 | 0 | assert(check(TokenKind::identifier) || tok_->isResWord()); |
1062 | 0 | SMLoc start = tok_->getStartLoc(); |
1063 | |
|
1064 | 0 | auto optName = parseTSQualifiedName(); |
1065 | 0 | if (!optName) |
1066 | 0 | return None; |
1067 | 0 | ESTree::Node *typeName = *optName; |
1068 | |
|
1069 | 0 | ESTree::Node *typeParams = nullptr; |
1070 | 0 | if (check(TokenKind::less)) { |
1071 | 0 | auto optTypeParams = parseTSTypeArguments(); |
1072 | 0 | if (!optTypeParams) |
1073 | 0 | return None; |
1074 | 0 | typeParams = *optTypeParams; |
1075 | 0 | } |
1076 | | |
1077 | 0 | return setLocation( |
1078 | 0 | start, |
1079 | 0 | getPrevTokenEndLoc(), |
1080 | 0 | new (context_) ESTree::TSTypeReferenceNode(typeName, typeParams)); |
1081 | 0 | } |
1082 | | |
1083 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSQualifiedName() { |
1084 | 0 | SMLoc start = tok_->getStartLoc(); |
1085 | 0 | ESTree::Node *typeName = setLocation( |
1086 | 0 | tok_, |
1087 | 0 | tok_, |
1088 | 0 | new (context_) ESTree::IdentifierNode( |
1089 | 0 | tok_->getResWordOrIdentifier(), nullptr, false)); |
1090 | 0 | advance(JSLexer::GrammarContext::Type); |
1091 | |
|
1092 | 0 | while (checkAndEat(TokenKind::period, JSLexer::GrammarContext::Type)) { |
1093 | 0 | if (!check(TokenKind::identifier) && !tok_->isResWord()) { |
1094 | 0 | errorExpected( |
1095 | 0 | TokenKind::identifier, |
1096 | 0 | "in qualified type name", |
1097 | 0 | "start of type name", |
1098 | 0 | start); |
1099 | 0 | return None; |
1100 | 0 | } |
1101 | 0 | ESTree::Node *right = setLocation( |
1102 | 0 | tok_, |
1103 | 0 | tok_, |
1104 | 0 | new (context_) ESTree::IdentifierNode( |
1105 | 0 | tok_->getResWordOrIdentifier(), nullptr, false)); |
1106 | 0 | advance(JSLexer::GrammarContext::Type); |
1107 | 0 | typeName = setLocation( |
1108 | 0 | typeName, |
1109 | 0 | getPrevTokenEndLoc(), |
1110 | 0 | new (context_) ESTree::TSQualifiedNameNode(typeName, right)); |
1111 | 0 | } |
1112 | | |
1113 | 0 | return typeName; |
1114 | 0 | } |
1115 | | |
1116 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSTypeQuery() { |
1117 | 0 | assert(check(TokenKind::rw_typeof)); |
1118 | 0 | SMLoc start = advance(JSLexer::GrammarContext::Type).Start; |
1119 | |
|
1120 | 0 | if (!(tok_->isResWord() || check(TokenKind::identifier))) { |
1121 | 0 | errorExpected( |
1122 | 0 | TokenKind::identifier, "in type query", "start of type query", start); |
1123 | 0 | return None; |
1124 | 0 | } |
1125 | | |
1126 | 0 | ESTree::Node *typeName = setLocation( |
1127 | 0 | tok_, |
1128 | 0 | tok_, |
1129 | 0 | new (context_) ESTree::IdentifierNode( |
1130 | 0 | tok_->getResWordOrIdentifier(), nullptr, false)); |
1131 | 0 | advance(JSLexer::GrammarContext::Type); |
1132 | |
|
1133 | 0 | while (checkAndEat(TokenKind::period, JSLexer::GrammarContext::Type)) { |
1134 | 0 | if (!check(TokenKind::identifier) && !tok_->isResWord()) { |
1135 | 0 | errorExpected( |
1136 | 0 | TokenKind::identifier, |
1137 | 0 | "in qualified type name", |
1138 | 0 | "start of type name", |
1139 | 0 | start); |
1140 | 0 | return None; |
1141 | 0 | } |
1142 | 0 | ESTree::Node *right = setLocation( |
1143 | 0 | tok_, |
1144 | 0 | tok_, |
1145 | 0 | new (context_) ESTree::IdentifierNode( |
1146 | 0 | tok_->getResWordOrIdentifier(), nullptr, false)); |
1147 | 0 | advance(JSLexer::GrammarContext::Type); |
1148 | 0 | typeName = setLocation( |
1149 | 0 | typeName, |
1150 | 0 | getPrevTokenEndLoc(), |
1151 | 0 | new (context_) ESTree::TSQualifiedNameNode(typeName, right)); |
1152 | 0 | } |
1153 | | |
1154 | 0 | return setLocation( |
1155 | 0 | start, |
1156 | 0 | getPrevTokenEndLoc(), |
1157 | 0 | new (context_) ESTree::TSTypeQueryNode(typeName)); |
1158 | 0 | } |
1159 | | |
1160 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSTypeArguments() { |
1161 | 0 | assert(check(TokenKind::less)); |
1162 | 0 | SMLoc start = advance(JSLexer::GrammarContext::Type).Start; |
1163 | |
|
1164 | 0 | ESTree::NodeList params{}; |
1165 | |
|
1166 | 0 | while (!check(TokenKind::greater)) { |
1167 | 0 | auto optType = parseTypeAnnotationTS(); |
1168 | 0 | if (!optType) |
1169 | 0 | return None; |
1170 | 0 | params.push_back(**optType); |
1171 | |
|
1172 | 0 | if (!checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) |
1173 | 0 | break; |
1174 | 0 | } |
1175 | | |
1176 | 0 | SMLoc end = tok_->getEndLoc(); |
1177 | 0 | if (!eat( |
1178 | 0 | TokenKind::greater, |
1179 | 0 | JSLexer::GrammarContext::Type, |
1180 | 0 | "at end of type parameters", |
1181 | 0 | "start of type parameters", |
1182 | 0 | start)) |
1183 | 0 | return None; |
1184 | | |
1185 | 0 | return setLocation( |
1186 | 0 | start, |
1187 | 0 | end, |
1188 | 0 | new (context_) |
1189 | 0 | ESTree::TSTypeParameterInstantiationNode(std::move(params))); |
1190 | 0 | } |
1191 | | |
1192 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSObjectType() { |
1193 | 0 | assert(check(TokenKind::l_brace)); |
1194 | 0 | SMLoc start = advance(JSLexer::GrammarContext::Type).Start; |
1195 | |
|
1196 | 0 | ESTree::NodeList members{}; |
1197 | |
|
1198 | 0 | while (!check(TokenKind::r_brace)) { |
1199 | 0 | auto optMember = parseTSObjectTypeMember(); |
1200 | 0 | if (!optMember) |
1201 | 0 | return None; |
1202 | 0 | members.push_back(**optMember); |
1203 | |
|
1204 | 0 | bool hasNext = check(TokenKind::comma, TokenKind::semi); |
1205 | 0 | if (hasNext) { |
1206 | 0 | advance(JSLexer::GrammarContext::Type); |
1207 | 0 | } else { |
1208 | 0 | break; |
1209 | 0 | } |
1210 | 0 | } |
1211 | | |
1212 | 0 | if (!eat( |
1213 | 0 | TokenKind::r_brace, |
1214 | 0 | JSLexer::GrammarContext::Type, |
1215 | 0 | "at end of object type", |
1216 | 0 | "start of object type", |
1217 | 0 | start)) |
1218 | 0 | return None; |
1219 | | |
1220 | 0 | return setLocation( |
1221 | 0 | start, |
1222 | 0 | getPrevTokenEndLoc(), |
1223 | 0 | new (context_) ESTree::TSTypeLiteralNode(std::move(members))); |
1224 | 0 | } |
1225 | | |
1226 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSObjectTypeMember() { |
1227 | 0 | SMLoc start = tok_->getStartLoc(); |
1228 | |
|
1229 | 0 | if (check(TokenKind::l_paren)) { |
1230 | 0 | ESTree::NodeList params{}; |
1231 | 0 | if (!parseTSFunctionTypeParams(start, params)) |
1232 | 0 | return None; |
1233 | 0 | ESTree::Node *returnType = nullptr; |
1234 | 0 | if (checkAndEat(TokenKind::colon, JSLexer::GrammarContext::Type)) { |
1235 | 0 | auto optType = parseTypeAnnotationTS(); |
1236 | 0 | if (!optType) |
1237 | 0 | return None; |
1238 | 0 | returnType = *optType; |
1239 | 0 | } |
1240 | 0 | return setLocation( |
1241 | 0 | start, |
1242 | 0 | getPrevTokenEndLoc(), |
1243 | 0 | new (context_) ESTree::TSCallSignatureDeclarationNode( |
1244 | 0 | std::move(params), returnType)); |
1245 | 0 | } |
1246 | | |
1247 | 0 | bool optional = false; |
1248 | 0 | bool computed = false; |
1249 | | |
1250 | | // TODO: Parse modifiers. |
1251 | 0 | bool readonly = false; |
1252 | 0 | bool isStatic = false; |
1253 | 0 | bool isExport = false; |
1254 | |
|
1255 | 0 | ESTree::Node *key = nullptr; |
1256 | | |
1257 | | // TODO: Parse initializer. |
1258 | 0 | ESTree::Node *init = nullptr; |
1259 | |
|
1260 | 0 | if (checkAndEat(TokenKind::l_square, JSLexer::GrammarContext::Type)) { |
1261 | 0 | computed = true; |
1262 | |
|
1263 | 0 | if (check(TokenKind::identifier)) { |
1264 | 0 | auto optNext = lexer_.lookahead1(llvh::None); |
1265 | 0 | if (optNext.hasValue() && *optNext == TokenKind::colon) { |
1266 | | // Unambiguously an index signature. |
1267 | 0 | return parseTSIndexSignature(start); |
1268 | 0 | } |
1269 | 0 | } |
1270 | | |
1271 | 0 | auto optExpr = parseAssignmentExpression(ParamIn); |
1272 | 0 | if (!optExpr) |
1273 | 0 | return None; |
1274 | 0 | key = *optExpr; |
1275 | |
|
1276 | 0 | if (!eat( |
1277 | 0 | TokenKind::r_square, |
1278 | 0 | JSLexer::GrammarContext::Type, |
1279 | 0 | "at end of computed property type", |
1280 | 0 | "start of property", |
1281 | 0 | start)) |
1282 | 0 | return None; |
1283 | | |
1284 | 0 | if (checkAndEat(TokenKind::question, JSLexer::GrammarContext::Type)) { |
1285 | 0 | optional = true; |
1286 | 0 | } |
1287 | 0 | } else { |
1288 | 0 | if (!need(TokenKind::identifier, "in property", "start of property", start)) |
1289 | 0 | return None; |
1290 | 0 | key = setLocation( |
1291 | 0 | tok_, |
1292 | 0 | tok_, |
1293 | 0 | new (context_) |
1294 | 0 | ESTree::IdentifierNode(tok_->getIdentifier(), nullptr, false)); |
1295 | 0 | advance(JSLexer::GrammarContext::Type); |
1296 | |
|
1297 | 0 | if (checkAndEat(TokenKind::question, JSLexer::GrammarContext::Type)) { |
1298 | 0 | optional = true; |
1299 | 0 | } |
1300 | 0 | } |
1301 | | |
1302 | 0 | if (check(TokenKind::colon)) { |
1303 | 0 | SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start; |
1304 | 0 | auto optType = parseTypeAnnotationTS(annotStart); |
1305 | 0 | if (!optType) |
1306 | 0 | return None; |
1307 | 0 | return setLocation( |
1308 | 0 | start, |
1309 | 0 | getPrevTokenEndLoc(), |
1310 | 0 | new (context_) ESTree::TSPropertySignatureNode( |
1311 | 0 | key, |
1312 | 0 | *optType, |
1313 | 0 | init, |
1314 | 0 | optional, |
1315 | 0 | computed, |
1316 | 0 | readonly, |
1317 | 0 | isStatic, |
1318 | 0 | isExport)); |
1319 | 0 | } |
1320 | | |
1321 | 0 | if (check(TokenKind::l_paren)) { |
1322 | 0 | ESTree::NodeList params{}; |
1323 | 0 | if (!parseTSFunctionTypeParams(start, params)) |
1324 | 0 | return None; |
1325 | | |
1326 | 0 | ESTree::Node *returnType = nullptr; |
1327 | 0 | if (check(TokenKind::colon)) { |
1328 | 0 | SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start; |
1329 | 0 | auto optType = parseTypeAnnotationTS(annotStart); |
1330 | 0 | if (!optType) |
1331 | 0 | return None; |
1332 | 0 | returnType = *optType; |
1333 | 0 | } |
1334 | | |
1335 | 0 | return setLocation( |
1336 | 0 | start, |
1337 | 0 | getPrevTokenEndLoc(), |
1338 | 0 | new (context_) ESTree::TSMethodSignatureNode( |
1339 | 0 | key, std::move(params), returnType, computed)); |
1340 | 0 | } |
1341 | | |
1342 | 0 | ESTree::Node *returnType = nullptr; |
1343 | 0 | if (check(TokenKind::colon)) { |
1344 | 0 | SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start; |
1345 | 0 | auto optType = parseTypeAnnotationTS(annotStart); |
1346 | 0 | if (!optType) |
1347 | 0 | return None; |
1348 | 0 | returnType = *optType; |
1349 | 0 | } |
1350 | | |
1351 | 0 | return setLocation( |
1352 | 0 | start, |
1353 | 0 | getPrevTokenEndLoc(), |
1354 | 0 | new (context_) ESTree::TSPropertySignatureNode( |
1355 | 0 | key, |
1356 | 0 | returnType, |
1357 | 0 | init, |
1358 | 0 | optional, |
1359 | 0 | computed, |
1360 | 0 | readonly, |
1361 | 0 | isStatic, |
1362 | 0 | isExport)); |
1363 | 0 | } |
1364 | | |
1365 | 0 | Optional<ESTree::Node *> JSParserImpl::parseTSIndexSignature(SMLoc start) { |
1366 | 0 | ESTree::NodeList params{}; |
1367 | |
|
1368 | 0 | while (!check(TokenKind::r_square)) { |
1369 | 0 | auto optKey = parseBindingIdentifier(Param{}); |
1370 | 0 | if (!optKey) { |
1371 | 0 | errorExpected( |
1372 | 0 | TokenKind::identifier, "in property", "start of property", start); |
1373 | |
|
1374 | 0 | return None; |
1375 | 0 | } |
1376 | 0 | params.push_back(**optKey); |
1377 | |
|
1378 | 0 | if (!checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type)) |
1379 | 0 | break; |
1380 | 0 | } |
1381 | | |
1382 | 0 | if (!eat( |
1383 | 0 | TokenKind::r_square, |
1384 | 0 | JSLexer::GrammarContext::Type, |
1385 | 0 | "at end of indexer type annotation", |
1386 | 0 | "start of indexer", |
1387 | 0 | start)) |
1388 | 0 | return None; |
1389 | | |
1390 | 0 | ESTree::Node *returnType = nullptr; |
1391 | 0 | if (check(TokenKind::colon)) { |
1392 | 0 | SMLoc annotStart = advance(JSLexer::GrammarContext::Type).Start; |
1393 | 0 | auto optType = parseTypeAnnotationTS(annotStart); |
1394 | 0 | if (!optType) |
1395 | 0 | return None; |
1396 | 0 | returnType = *optType; |
1397 | 0 | } |
1398 | | |
1399 | 0 | return setLocation( |
1400 | 0 | start, |
1401 | 0 | getPrevTokenEndLoc(), |
1402 | 0 | new (context_) |
1403 | 0 | ESTree::TSIndexSignatureNode(std::move(params), returnType)); |
1404 | 0 | } |
1405 | | |
1406 | | ESTree::Node *JSParserImpl::reparseIdentifierAsTSTypeAnnotation( |
1407 | 0 | ESTree::IdentifierNode *ident) { |
1408 | 0 | UniqueString *name = ident->_name; |
1409 | 0 | if (name == anyIdent_) { |
1410 | 0 | return setLocation(ident, ident, new (context_) ESTree::TSAnyKeywordNode()); |
1411 | 0 | } |
1412 | 0 | if (name == booleanIdent_) { |
1413 | 0 | return setLocation( |
1414 | 0 | ident, ident, new (context_) ESTree::TSBooleanKeywordNode()); |
1415 | 0 | } |
1416 | 0 | if (name == numberIdent_) { |
1417 | 0 | return setLocation( |
1418 | 0 | ident, ident, new (context_) ESTree::TSNumberKeywordNode()); |
1419 | 0 | } |
1420 | 0 | if (name == symbolIdent_) { |
1421 | 0 | return setLocation( |
1422 | 0 | ident, ident, new (context_) ESTree::TSSymbolKeywordNode()); |
1423 | 0 | } |
1424 | 0 | if (name == stringIdent_) { |
1425 | 0 | return setLocation( |
1426 | 0 | ident, ident, new (context_) ESTree::TSStringKeywordNode()); |
1427 | 0 | } |
1428 | | |
1429 | 0 | return setLocation( |
1430 | 0 | ident, ident, new (context_) ESTree::TSTypeReferenceNode(ident, {})); |
1431 | 0 | } |
1432 | | |
1433 | | #endif |
1434 | | |
1435 | | } // namespace detail |
1436 | | } // namespace parser |
1437 | | } // namespace hermes |