rslint_parser/syntax/
typescript.rs

1//! TypeScript specific functions.
2
3use super::decl::*;
4use super::expr::{assign_expr, identifier_name, lhs_expr, literal};
5use super::stmt::{block_items, semi, var_decl};
6use crate::{SyntaxKind::*, *};
7
8pub const BASE_TS_RECOVERY_SET: TokenSet = token_set![
9    T![void],
10    T![ident],
11    T![ident],
12    T![await],
13    T![null],
14    T![break],
15    T!['['],
16];
17
18#[rustfmt::skip]
19pub const DISALLOWED_TYPE_NAMES: &[&str] = &[
20    "string",
21    "null",
22    "number",
23    "object",
24    "any",
25    "unknown",
26    "boolean",
27    "bigint",
28    "symbol",
29    "void",
30    "never",
31];
32
33// ambiguity is fun!
34macro_rules! no_recover {
35    ($p:expr, $res:expr) => {
36        if $res.is_none() && $p.state.no_recovery {
37            return None;
38        }
39    };
40}
41
42pub(crate) fn abstract_readonly_modifiers(
43    p: &mut Parser,
44) -> (Option<Range<usize>>, Option<Range<usize>>) {
45    let (mut abstract_, mut readonly) = (None, None);
46    for _ in 0..2 {
47        match p.cur_src() {
48            "abstract" if abstract_.is_none() => {
49                abstract_ = ts_modifier(p, &["abstract"]);
50                if abstract_.is_none() {
51                    return (abstract_, readonly);
52                }
53            }
54            "readonly" if readonly.is_none() => {
55                readonly = ts_modifier(p, &["readonly"]);
56                if readonly.is_none() {
57                    return (abstract_, readonly);
58                }
59            }
60            _ => return (abstract_, readonly),
61        }
62    }
63    (abstract_, readonly)
64}
65
66pub fn ts_modifier(p: &mut Parser, modifiers: &[&'static str]) -> Option<Range<usize>> {
67    if !modifiers.contains(&p.cur_src()) {
68        return None;
69    }
70
71    let range = p.cur_tok().range;
72
73    if p.has_linebreak_before_n(1)
74        || token_set![T!['('], T![')'], T![:], T![=], T![?]].contains(p.nth(1))
75    {
76        return None;
77    }
78
79    let kind = match p.cur_src() {
80        "abstract" => T![abstract],
81        "readonly" => T![readonly],
82        _ => unreachable!("unknown modifier"),
83    };
84    p.bump_remap(kind);
85
86    Some(range)
87}
88
89pub(crate) fn maybe_ts_type_annotation(p: &mut Parser) -> Option<Range<usize>> {
90    if p.at(T![:]) {
91        let maybe_err = p.start();
92        let start = p.cur_tok().range.start;
93        p.bump_any();
94        let compl = ts_type(p);
95        let end = compl
96            .map(|x| usize::from(x.range(p).end()))
97            .unwrap_or_else(|| p.cur_tok().range.start);
98
99        if !p.typescript() {
100            let err = p
101                .err_builder("type annotations can only be used in TypeScript files")
102                .primary(start..end, "");
103
104            p.error(err);
105            maybe_err.complete(p, ERROR);
106        } else {
107            maybe_err.abandon(p);
108        }
109        Some(start..end)
110    } else {
111        None
112    }
113}
114
115pub(crate) fn ts_expr_stmt(p: &mut Parser) -> Option<CompletedMarker> {
116    match p.cur_src() {
117        "declare" => ts_declare(p),
118        "global" => {
119            if p.nth_at(1, T!['{']) {
120                ts_ambient_external_module_decl(p, false)
121            } else {
122                None
123            }
124        }
125        _ => ts_decl(p),
126    }
127}
128
129pub(crate) fn ts_declare(p: &mut Parser) -> Option<CompletedMarker> {
130    debug_assert_eq!(p.cur_src(), "declare");
131    let p = &mut *p.with_state(ParserState {
132        in_declare: true,
133        ..p.state.clone()
134    });
135    Some(match p.nth(1) {
136        T![function] => {
137            p.state.decorators_were_valid = true;
138            let m = p.start();
139            p.bump_remap(T![declare]);
140            function_decl(p, m, false)
141        }
142        T![class] => {
143            p.state.decorators_were_valid = true;
144            let m = p.start();
145            p.bump_remap(T![declare]);
146            class_decl(p, false).undo_completion(p).abandon(p);
147            m.complete(p, CLASS_DECL)
148        }
149        t if (t == T![const] && p.nth_at(2, T![enum])) || t == T![enum] => {
150            let m = p.start();
151            p.bump_remap(T![declare]);
152            ts_enum(p).undo_completion(p).abandon(p);
153            m.complete(p, TS_ENUM)
154        }
155        T![const] | T![var] => {
156            let m = p.start();
157            p.bump_remap(T![declare]);
158            // unwrap the marker so its children go to `m`
159            var_decl(p, false).undo_completion(p).abandon(p);
160            m.complete(p, VAR_DECL)
161        }
162        _ if p.nth_src(1) == "let" => {
163            let m = p.start();
164            p.bump_remap(T![declare]);
165            var_decl(p, false).undo_completion(p).abandon(p);
166            m.complete(p, VAR_DECL)
167        }
168        _ if p.nth_src(1) == "global" => {
169            let m = p.start();
170            p.bump_remap(T![declare]);
171            if let Some(complete) = ts_ambient_external_module_decl(p, false) {
172                complete.undo_completion(p).abandon(p);
173            }
174            m.complete(p, TS_MODULE_DECL)
175        }
176        _ => {
177            let checkpoint = p.checkpoint();
178            let m = p.start();
179            p.bump_remap(T![declare]);
180            let res = ts_decl(p);
181            if let Some(res) = res {
182                let kind = res.kind();
183                res.undo_completion(p).abandon(p);
184                return Some(m.complete(p, kind));
185            } else {
186                m.abandon(p);
187                p.rewind(checkpoint);
188                return None;
189            }
190        }
191    })
192}
193
194pub(crate) fn ts_decl(p: &mut Parser) -> Option<CompletedMarker> {
195    if p.cur_src() == "abstract" {
196        p.state.decorators_were_valid = true;
197        let m = p.start();
198        let range = p.cur_tok().range;
199        p.bump_remap(T![abstract]);
200        if !p.at(T![class]) {
201            let err = p.err_builder("abstract modifiers can only be applied to classes, methods, or property definitions")
202                .primary(range, "");
203
204            p.error(err);
205            return None;
206        }
207        class_decl(p, false).undo_completion(p).abandon(p);
208        return Some(m.complete(p, CLASS_DECL));
209    }
210
211    if p.at(T![enum]) {
212        return Some(ts_enum(p));
213    }
214
215    if p.cur_src() == "interface" {
216        return ts_interface(p);
217    }
218
219    if p.cur_src() == "module" {
220        if p.nth_at(1, STRING) {
221            return ts_ambient_external_module_decl(p, true);
222        } else if token_set![T![ident], T![yield], T![await]].contains(p.nth(1)) {
223            p.bump_remap(T![module]);
224            return ts_module_or_namespace_decl(p, false, false);
225        }
226    }
227
228    if p.cur_src() == "namespace" {
229        let m = p.start();
230        p.bump_any();
231        ts_module_or_namespace_decl(p, true, false)?
232            .undo_completion(p)
233            .abandon(p);
234        return Some(m.complete(p, TS_NAMESPACE_DECL));
235    }
236
237    if p.cur_src() == "type" {
238        return ts_type_alias_decl(p);
239    }
240
241    None
242}
243
244pub fn ts_type_alias_decl(p: &mut Parser) -> Option<CompletedMarker> {
245    let m = p.start();
246    let start = p.cur_tok().range.start;
247    p.bump_any();
248    no_recover!(p, identifier_name(p));
249    if p.at(T![<]) {
250        no_recover!(p, ts_type_params(p));
251    }
252    let end = p.cur_tok().range.end;
253    p.expect_no_recover(T![=])?;
254    no_recover!(p, ts_type(p));
255    semi(p, start..end);
256    Some(m.complete(p, TS_TYPE_ALIAS_DECL))
257}
258
259pub(crate) fn ts_module_or_namespace_decl(
260    p: &mut Parser,
261    namespace: bool,
262    eat_dot: bool,
263) -> Option<CompletedMarker> {
264    let m = p.start();
265    if eat_dot {
266        p.eat(T![.]);
267    }
268
269    if identifier_name(p).is_none() && p.state.no_recovery {
270        return None;
271    }
272
273    if p.at(T![.]) {
274        ts_module_or_namespace_decl(p, namespace, true)?;
275    } else {
276        ts_module_block(p);
277    }
278
279    Some(m.complete(
280        p,
281        if namespace {
282            TS_NAMESPACE_DECL
283        } else {
284            TS_MODULE_DECL
285        },
286    ))
287}
288
289pub fn ts_ambient_external_module_decl(
290    p: &mut Parser,
291    check_for_module: bool,
292) -> Option<CompletedMarker> {
293    let m = p.start();
294    let start = p.cur_tok().range.start;
295    if check_for_module && p.cur_src() != "module" {
296        let err = p
297            .err_builder(&format!(
298                "expected keyword `module`, but instead found `{}`",
299                p.cur_src()
300            ))
301            .primary(p.cur_tok().range, "");
302
303        p.error(err);
304    } else if check_for_module {
305        p.bump_remap(T![module]);
306    }
307
308    let end = p.cur_tok().range.end;
309    if p.cur_src() == "global" {
310        p.bump_any();
311    } else {
312        p.expect(STRING);
313    }
314    if p.at(T!['{']) {
315        ts_module_block(p);
316    } else {
317        semi(p, start..end);
318    }
319    Some(m.complete(p, TS_MODULE_DECL))
320}
321
322pub fn ts_module_block(p: &mut Parser) -> Option<CompletedMarker> {
323    let m = p.start();
324    p.expect_no_recover(T!['{'])?;
325    // module blocks are considered top level
326    block_items(p, false, true, true, None);
327    p.expect_no_recover(T!['}'])?;
328    Some(m.complete(p, TS_MODULE_BLOCK))
329}
330
331pub fn ts_interface(p: &mut Parser) -> Option<CompletedMarker> {
332    let m = p.start();
333    if p.cur_src() != "interface" {
334        let err = p
335            .err_builder(&format!(
336                "expected keyword `interface`, but instead found `{}`",
337                p.cur_src()
338            ))
339            .primary(p.cur_tok().range, "");
340
341        p.error(err);
342    } else {
343        p.bump_any();
344    }
345
346    if DISALLOWED_TYPE_NAMES.contains(&p.cur_src()) || p.cur_src() == "intrinsic" {
347        let err = p
348            .err_builder(&format!(
349                "`{}` cannot be used as the name of an interface",
350                p.cur_src()
351            ))
352            .primary(p.cur_tok().range, "")
353            .footer_note(format!("`{}` is already reserved as a type", p.cur_src()));
354
355        p.error(err);
356    }
357    identifier_name(p);
358    if p.at(T![<]) {
359        ts_type_params(p);
360    }
361
362    if p.cur_src() == "extends" {
363        p.bump_any();
364        ts_heritage_clause(p, false);
365    };
366
367    while p.cur_src() == "extends" {
368        let m = p.start();
369        p.bump_any();
370        let mut complete = ts_heritage_clause(p, false);
371        for elem in &mut complete {
372            elem.change_kind(p, ERROR);
373        }
374
375        let err = p
376            .err_builder("interfaces cannot contain multiple `extends` clauses")
377            .primary(p.marker_vec_range(&complete), "");
378
379        p.error(err);
380        m.complete(p, ERROR);
381    }
382    p.expect(T!['{']);
383    while !p.at(EOF) && !p.at(T!['}']) {
384        ts_type_member(p);
385    }
386    p.expect(T!['}']);
387    Some(m.complete(p, TS_INTERFACE_DECL))
388}
389
390// FIXME: ts allows trailing commas but this doesnt, we need to figure out a way
391// to peek at the next token and see if its the end of the heritage clause
392pub(crate) fn ts_heritage_clause(p: &mut Parser, exprs: bool) -> Vec<CompletedMarker> {
393    let mut elems = Vec::with_capacity(1);
394    let m = p.start();
395    if exprs {
396        lhs_expr(p);
397    } else {
398        ts_entity_name(p, None, false);
399    }
400    if p.at(T![<]) {
401        ts_type_args(p);
402    }
403    // it doesnt matter if we complete as ts_expr_with_type_args even if its an lhs expr
404    // because exprs: true will only be used with `class extends foo, bar`, in which case
405    // the first expr with be "unwrapped" to go to the class' node and the rest are errors
406    elems.push(m.complete(p, TS_EXPR_WITH_TYPE_ARGS));
407
408    while p.eat(T![,]) {
409        let m = p.start();
410        if exprs {
411            lhs_expr(p);
412        } else {
413            ts_entity_name(p, None, false);
414        }
415        if p.at(T![<]) {
416            ts_type_args(p);
417        }
418        elems.push(m.complete(p, TS_EXPR_WITH_TYPE_ARGS));
419    }
420    elems
421}
422
423pub fn ts_type_member(p: &mut Parser) -> Option<CompletedMarker> {
424    if p.at(T!['(']) || p.at(T![<]) {
425        return ts_signature_member(p, false);
426    }
427
428    if p.at(T![new]) && token_set!(T![<], T!['(']).contains(p.nth(1)) {
429        return ts_signature_member(p, true);
430    }
431
432    let (m, readonly) = if p.cur_src() == "readonly" {
433        let m = p.start();
434        p.bump_remap(T![readonly]);
435        (m, true)
436    } else {
437        (p.start(), false)
438    };
439
440    if let Some(idx) = try_parse_index_signature(p, m.clone()) {
441        return Some(idx);
442    }
443
444    ts_property_or_method_sig(p, m, readonly)
445}
446
447fn ts_property_or_method_sig(p: &mut Parser, m: Marker, readonly: bool) -> Option<CompletedMarker> {
448    if p.eat(T!['[']) {
449        assign_expr(p);
450        p.expect_no_recover(T![']'])?;
451    } else {
452        match p.cur() {
453            STRING | NUMBER => {
454                literal(p);
455            }
456            _ => {
457                let mut complete = maybe_private_name(p)?;
458                if complete.kind() == PRIVATE_NAME {
459                    let err = p
460                        .err_builder("private names are not allowed outside of class bodies")
461                        .primary(complete.range(p), "");
462
463                    p.error(err);
464                    complete.change_kind(p, ERROR);
465                }
466            }
467        }
468    };
469
470    p.eat(T![?]);
471    Some(if !readonly && p.at_ts(token_set![T!['('], T![<]]) {
472        if p.at(T![<]) {
473            no_recover!(p, ts_type_params(p));
474        }
475        formal_parameters(p);
476        if p.at(T![:]) {
477            ts_type_or_type_predicate_ann(p, T![:]);
478        }
479        type_member_semi(p);
480        m.complete(p, TS_METHOD_SIGNATURE)
481    } else {
482        if p.eat(T![:]) {
483            ts_type(p);
484        }
485        type_member_semi(p);
486        m.complete(p, TS_PROPERTY_SIGNATURE)
487    })
488}
489
490pub(crate) fn try_parse_index_signature(p: &mut Parser, m: Marker) -> Option<CompletedMarker> {
491    if !p.at(T!['['])
492        || !(token_set![T![ident], T![await], T![yield]].contains(p.nth(1))
493            || p.nth(1).is_keyword())
494        || !token_set![T![:], T![,]].contains(p.nth(2))
495    {
496        return None;
497    }
498
499    p.expect_no_recover(T!['['])?;
500    let pat_m = p.start();
501    identifier_name(p);
502    p.expect_no_recover(T![:])?;
503    no_recover!(p, ts_type(p));
504    pat_m.complete(p, SINGLE_PATTERN);
505    p.expect_no_recover(T![']'])?;
506
507    if p.eat(T![:]) {
508        no_recover!(p, ts_type(p));
509    }
510    type_member_semi(p);
511    Some(m.complete(p, TS_INDEX_SIGNATURE))
512}
513
514pub fn ts_signature_member(p: &mut Parser, construct_sig: bool) -> Option<CompletedMarker> {
515    let m = p.start();
516    if construct_sig {
517        p.expect(T![new]);
518    }
519
520    if p.at(T![<]) {
521        no_recover!(p, ts_type_params(p));
522    }
523
524    formal_parameters(&mut *p.with_state(ParserState {
525        in_binding_list_for_signature: true,
526        ..p.state.clone()
527    }));
528    if p.at(T![:]) {
529        no_recover!(p, ts_type_or_type_predicate_ann(p, T![:]));
530    }
531    type_member_semi(p);
532
533    Some(m.complete(
534        p,
535        if construct_sig {
536            TS_CONSTRUCT_SIGNATURE_DECL
537        } else {
538            TS_CALL_SIGNATURE_DECL
539        },
540    ))
541}
542
543// TODO(RDambrosio016): is this logic correct?
544fn type_member_semi(p: &mut Parser) {
545    if p.at_ts(token_set![T![,], T![;]]) {
546        p.bump_any();
547    }
548}
549
550pub fn ts_enum(p: &mut Parser) -> CompletedMarker {
551    let m = p.start();
552    p.eat(T![const]);
553    p.expect(T![enum]);
554    identifier_name(p);
555    p.expect(T!['{']);
556    let mut first = true;
557
558    while !p.at(EOF) && !p.at(T!['}']) {
559        if first {
560            first = false;
561        } else if p.at(T![,]) && p.nth_at(1, T!['}']) {
562            p.eat(T![,]);
563            break;
564        } else {
565            p.expect(T![,]);
566        }
567
568        let member = p.start();
569        let err_occured = if !p.at_ts(token_set![T![ident], T![yield], T![await]])
570            && !p.cur().is_keyword()
571            && !p.at(STRING)
572        {
573            let err = p
574                .err_builder("expected an identifier or string for an enum variant, but found none")
575                .primary(p.cur_tok().range, "");
576
577            p.err_recover(
578                err,
579                token_set![T!['}'], T![ident], T![yield], T![await], T![=], T![,]],
580                false,
581            );
582            true
583        } else {
584            if !p.eat(STRING) {
585                identifier_name(p).unwrap().undo_completion(p).abandon(p);
586            }
587            false
588        };
589
590        if p.eat(T![=]) {
591            assign_expr(p);
592            member.complete(p, TS_ENUM_MEMBER);
593        } else if err_occured {
594            member.abandon(p);
595        } else {
596            member.complete(p, TS_ENUM_MEMBER);
597        }
598    }
599
600    p.expect(T!['}']);
601    m.complete(p, TS_ENUM)
602}
603
604pub fn try_parse_ts(
605    p: &mut Parser,
606    func: impl FnOnce(&mut Parser) -> Option<CompletedMarker>,
607) -> Option<CompletedMarker> {
608    // FIXME: simply rewinding doesnt work for `new A < T;` and it makes the parser enter unreachable
609    // code, and i really cant be bothered to debug incorrect event code because i don't have enough sanity
610    // left in me
611    let old = p.to_owned();
612    let res = func(&mut *p.with_state(ParserState {
613        no_recovery: true,
614        ..p.state.clone()
615    }));
616    if res.is_none() {
617        *p = old;
618    }
619    res
620}
621
622pub fn ts_type(p: &mut Parser) -> Option<CompletedMarker> {
623    let ty = ts_non_conditional_type(p);
624    if p.has_linebreak_before_n(0) || !p.at(T![extends]) {
625        return ty;
626    }
627
628    let m = ty.map(|x| x.precede(p)).unwrap_or_else(|| p.start());
629    if p.at(T![extends]) {
630        let m = p.start();
631        p.bump_any();
632        no_recover!(p, ts_non_conditional_type(p));
633        m.complete(p, TS_EXTENDS);
634    }
635    p.expect_no_recover(T![?])?;
636    no_recover!(p, ts_type(p));
637    p.expect_no_recover(T![:])?;
638    no_recover!(p, ts_type(p));
639    Some(m.complete(p, TS_CONDITIONAL_TYPE))
640}
641
642pub fn ts_fn_or_constructor_type(p: &mut Parser, fn_type: bool) -> Option<CompletedMarker> {
643    let m = p.start();
644    if !fn_type {
645        p.expect_no_recover(T![new])?;
646    }
647
648    if p.at(T![<]) {
649        ts_type_params(p);
650    }
651    formal_parameters(p);
652    no_recover!(p, ts_type_or_type_predicate_ann(p, T![=>]));
653    Some(m.complete(
654        p,
655        if fn_type {
656            TS_FN_TYPE
657        } else {
658            TS_CONSTRUCTOR_TYPE
659        },
660    ))
661}
662
663pub(crate) fn ts_type_or_type_predicate_ann(
664    p: &mut Parser,
665    return_token: SyntaxKind,
666) -> Option<CompletedMarker> {
667    let ident_ref_set = token_set![T![await], T![yield], T![ident]];
668    p.expect_no_recover(return_token)?;
669
670    let type_pred = (p.cur_src() == "asserts" && ident_ref_set.contains(p.nth(1)))
671        || (p.at_ts(ident_ref_set) && p.nth_src(1) == "is" && !p.has_linebreak_before_n(1));
672
673    if type_pred {
674        ts_predicate(p)
675    } else {
676        ts_type(p)
677    }
678}
679
680pub fn ts_non_conditional_type(p: &mut Parser) -> Option<CompletedMarker> {
681    if is_start_of_fn_type(p) {
682        return ts_fn_or_constructor_type(p, true);
683    }
684
685    if p.at(T![new]) {
686        return ts_fn_or_constructor_type(p, false);
687    }
688
689    intersection_or_union(p, false, |p| ts_intersection_type_or_higher(p), T![|])
690}
691
692fn ts_intersection_type_or_higher(p: &mut Parser) -> Option<CompletedMarker> {
693    intersection_or_union(p, true, |p| ts_type_operator_or_higher(p), T![&])
694}
695
696fn look_ahead(p: &mut Parser, func: impl FnOnce(&mut Parser) -> bool) -> bool {
697    let checkpoint = p.checkpoint();
698    let res = func(p);
699    p.rewind(checkpoint);
700    res
701}
702
703fn is_start_of_fn_type(p: &mut Parser) -> bool {
704    p.at(T![<]) || (p.at(T!['(']) && look_ahead(p, is_unambiguously_start_of_fn_type))
705}
706
707fn is_unambiguously_start_of_fn_type(p: &mut Parser) -> bool {
708    p.eat(T!['(']);
709    if p.at(T![')']) || p.at(T![...]) {
710        return true;
711    }
712
713    if skip_parameter_start(p) {
714        if p.at_ts(token_set![T![:], T![,], T![?], T![=]]) {
715            return true;
716        }
717        if p.at(T![')']) && p.nth_at(1, T![=>]) {
718            return true;
719        }
720    }
721    false
722}
723
724fn skip_parameter_start(p: &mut Parser) -> bool {
725    maybe_eat_incorrect_modifier(p);
726    if p.at_ts(token_set![T![this], T![yield], T![ident], T![await]]) {
727        p.bump_any();
728        return true;
729    }
730
731    if p.eat(T!['{']) {
732        let mut counter = 1;
733
734        while counter > 0 {
735            if p.eat(T!['{']) {
736                counter += 1;
737            } else if p.eat(T!['}']) {
738                counter -= 1;
739            } else {
740                p.bump_any();
741            }
742        }
743        return true;
744    }
745
746    if p.eat(T!['[']) {
747        let mut counter = 1;
748
749        while counter > 0 {
750            if p.eat(T!['[']) {
751                counter += 1;
752            } else if p.eat(T![']']) {
753                counter -= 1;
754            } else {
755                p.bump_any();
756            }
757        }
758        return true;
759    }
760    false
761}
762
763fn intersection_or_union(
764    p: &mut Parser,
765    intersection: bool,
766    mut constituent: impl FnMut(&mut Parser) -> Option<CompletedMarker>,
767    op: SyntaxKind,
768) -> Option<CompletedMarker> {
769    let kind = if intersection {
770        TS_INTERSECTION
771    } else {
772        TS_UNION
773    };
774    let m = p.start();
775    let saw_op = p.eat(op);
776    let ty = constituent(p);
777    if p.at(op) {
778        while p.eat(op) {
779            constituent(p);
780        }
781
782        Some(m.complete(p, kind))
783    } else if !saw_op && ty.is_none() {
784        m.abandon(p);
785        None
786    } else if !saw_op {
787        m.abandon(p);
788        ty
789    } else {
790        Some(m.complete(p, kind))
791    }
792}
793
794pub fn ts_type_operator_or_higher(p: &mut Parser) -> Option<CompletedMarker> {
795    if matches!(p.cur_src(), "keyof" | "unique" | "readonly") {
796        let m = p.start();
797        let kind = match p.cur_src() {
798            "keyof" => KEYOF_KW,
799            "unique" => UNIQUE_KW,
800            "readonly" => READONLY_KW,
801            _ => unreachable!(),
802        };
803        p.bump_remap(kind);
804        no_recover!(p, ts_type_operator_or_higher(p));
805        Some(m.complete(p, TS_TYPE_OPERATOR))
806    } else if p.cur_src() == "infer" {
807        let m = p.start();
808        p.bump_remap(T![infer]);
809        identifier_name(p);
810        Some(m.complete(p, TS_INFER))
811    } else {
812        // FIXME: readonly should apparently be handled here?
813        // but the previous matches should have accounted for it 🤔
814        ts_array_type_or_higher(p)
815    }
816}
817
818pub fn ts_array_type_or_higher(p: &mut Parser) -> Option<CompletedMarker> {
819    let mut ty = ts_non_array_type(p);
820
821    while !p.has_linebreak_before_n(0) && p.at(T!['[']) {
822        let m = ty.map(|x| x.precede(p)).unwrap_or_else(|| p.start());
823        p.bump_any();
824        if p.eat(T![']']) {
825            ty = Some(m.complete(p, TS_ARRAY));
826        } else {
827            no_recover!(p, ts_type(p));
828            p.expect_no_recover(T![']'])?;
829            ty = Some(m.complete(p, TS_INDEXED_ARRAY));
830        }
831    }
832    ty
833}
834
835pub fn ts_tuple(p: &mut Parser) -> Option<CompletedMarker> {
836    let m = p.start();
837    p.expect_no_recover(T!['['])?;
838
839    while !p.at(EOF) && !p.at(T![']']) {
840        let m = p.start();
841        let rest_range = p.cur_tok().range;
842        let rest = p.eat(T![...]);
843        let name = if crate::at_ident_name!(p)
844            && !DISALLOWED_TYPE_NAMES.contains(&p.cur_src())
845            && (p.nth_at(1, T![:]) || (p.nth_at(1, T![?]) && p.nth_at(2, T![:])))
846        {
847            identifier_name(p);
848            true
849        } else {
850            false
851        };
852
853        let opt_range = p.cur_tok().range;
854        let is_opt = name && p.eat(T![?]);
855        if name {
856            p.expect(T![:]);
857        }
858        no_recover!(p, ts_type(p));
859        if !name && p.at(T![?]) {
860            p.eat(T![?]);
861        }
862        m.complete(p, TS_TUPLE_ELEMENT);
863        if is_opt && rest {
864            let err = p
865                .err_builder("a tuple element cannot be both rest and optional")
866                .secondary(rest_range, "")
867                .primary(opt_range, "");
868
869            p.error(err);
870        }
871        p.eat(T![,]);
872    }
873
874    p.expect_no_recover(T![']'])?;
875    Some(m.complete(p, TS_TUPLE))
876}
877
878pub fn ts_non_array_type(p: &mut Parser) -> Option<CompletedMarker> {
879    match p.cur() {
880        T![ident] | T![void] | T![yield] | T![null] | T![await] | T![break] => {
881            if p.cur_src() == "asserts" && p.nth_at(1, T![this]) {
882                p.bump_any();
883                return ts_predicate(p);
884            }
885
886            let kind = match p.cur_src() {
887                "void" => TS_VOID,
888                "null" => TS_NULL,
889                "any" => TS_ANY,
890                "boolean" => TS_BOOLEAN,
891                "bigint" => TS_BIGINT,
892                "never" => TS_NEVER,
893                "number" => TS_NUMBER,
894                "object" => TS_OBJECT,
895                "string" => TS_STRING,
896                "symbol" => TS_SYMBOL,
897                "unknown" => TS_UNKNOWN,
898                "undefined" => TS_UNDEFINED,
899                _ =>
900                /* dummy value */
901                {
902                    ERROR
903                }
904            };
905
906            if kind != ERROR && !p.nth_at(1, T![.]) {
907                let m = p.start();
908                p.bump_any();
909                Some(m.complete(p, kind))
910            } else {
911                ts_type_ref(p, None)
912            }
913        }
914        NUMBER | STRING | TRUE_KW | FALSE_KW | REGEX => {
915            Some(literal(p).unwrap().precede(p).complete(p, TS_LITERAL))
916        }
917        BACKTICK => {
918            let m = p.start();
919            p.bump_any();
920
921            while !p.at(EOF) && !p.at(BACKTICK) {
922                match p.cur() {
923                    TEMPLATE_CHUNK => p.bump_any(),
924                    DOLLARCURLY => {
925                        let e = p.start();
926                        p.bump_any();
927                        ts_type(p);
928                        p.expect(T!['}']);
929                        e.complete(p, TS_TEMPLATE_ELEMENT);
930                    },
931                    t => unreachable!("Anything not template chunk or dollarcurly should have been eaten by the lexer, but {:?} was found", t),
932                }
933            }
934
935            p.eat(BACKTICK);
936            Some(m.complete(p, TS_TEMPLATE))
937        }
938        T![-] => {
939            let m = p.start();
940            p.bump_any();
941            if p.at(NUMBER) {
942                let _m = p.start();
943                p.bump_any();
944                _m.complete(p, LITERAL);
945            } else {
946                p.expect_no_recover(NUMBER)?;
947            }
948            Some(m.complete(p, TS_LITERAL))
949        }
950        T![import] => ts_import(p),
951        T![this] => {
952            if p.nth_src(1) == "is" {
953                ts_predicate(p)
954            } else {
955                let m = p.start();
956                p.bump_any();
957                Some(m.complete(p, TS_THIS))
958            }
959        }
960        T![typeof] => ts_type_query(p),
961        T!['{'] => {
962            if is_mapped_type_start(p) {
963                ts_mapped_type(p)
964            } else {
965                let m = p.start();
966                p.bump_any();
967                while !p.at(EOF) && !p.at(T!['}']) {
968                    ts_type_member(p);
969                    type_member_semi(p);
970                }
971                p.expect(T!['}']);
972                Some(m.complete(p, TS_OBJECT_TYPE))
973            }
974        }
975        T!['['] => ts_tuple(p),
976        T!['('] => {
977            let m = p.start();
978            p.bump_any();
979            no_recover!(p, ts_type(p));
980            p.expect_no_recover(T![')'])?;
981            Some(m.complete(p, TS_PAREN))
982        }
983        _ => {
984            let err = p
985                .err_builder("expected a type")
986                .primary(p.cur_tok().range, "");
987
988            p.err_recover(
989                err,
990                BASE_TS_RECOVERY_SET.union(token_set![
991                    T![typeof],
992                    T!['{'],
993                    T!['['],
994                    T!['('],
995                    T![this],
996                    T![import],
997                    T![-],
998                    NUMBER,
999                    STRING,
1000                    TRUE_KW,
1001                    FALSE_KW,
1002                    REGEX,
1003                    BACKTICK,
1004                    T![&],
1005                    T![|]
1006                ]),
1007                false,
1008            );
1009            None
1010        }
1011    }
1012}
1013
1014pub fn ts_type_args(p: &mut Parser) -> Option<CompletedMarker> {
1015    let m = p.start();
1016    p.expect_no_recover(T![<])?;
1017    let mut first = true;
1018
1019    while !p.at(EOF) && !p.at(T![>]) {
1020        if first {
1021            first = false;
1022        } else if p.at(T![,]) && p.nth_at(1, T![>]) {
1023            let m = p.start();
1024            let range = p.cur_tok().range;
1025            p.bump_any();
1026            m.complete(p, ERROR);
1027            let err = p
1028                .err_builder("type arguments may not contain trailing commas")
1029                .primary(range, "help: remove this comma");
1030
1031            p.error(err);
1032        } else {
1033            p.expect_no_recover(T![,])?;
1034        }
1035        no_recover!(p, ts_type(p));
1036    }
1037    p.expect_no_recover(T![>])?;
1038    Some(m.complete(p, TS_TYPE_ARGS))
1039}
1040
1041// FIXME: `<T() => {}` causes infinite recursion if the parser isnt being run with `no_recovery`
1042pub fn ts_type_params(p: &mut Parser) -> Option<CompletedMarker> {
1043    let m = p.start();
1044    p.expect_no_recover(T![<])?;
1045    let mut first = true;
1046
1047    while !p.at(EOF) && !p.at(T![>]) {
1048        if first {
1049            first = false;
1050        } else {
1051            if p.at(T![,]) && p.nth_at(1, T![>]) {
1052                p.bump_any();
1053                break;
1054            }
1055            p.expect_no_recover(T![,])?;
1056        }
1057        no_recover!(p, type_param(p));
1058    }
1059    p.expect_no_recover(T![>])?;
1060    Some(m.complete(p, TS_TYPE_PARAMS))
1061}
1062
1063fn type_param(p: &mut Parser) -> Option<CompletedMarker> {
1064    let m = p.start();
1065    let mut should_complete =
1066        if p.at_ts(token_set![T![ident], T![await], T![yield]]) || p.cur().is_keyword() {
1067            p.bump_remap(T![ident]);
1068            true
1069        } else {
1070            false
1071        };
1072    if p.cur_src() == "extends" {
1073        should_complete = true;
1074        let _m = p.start();
1075        p.bump_remap(T![extends]);
1076        no_recover!(p, ts_type(p));
1077        _m.complete(p, TS_CONSTRAINT);
1078    }
1079    if p.at(T![=]) {
1080        should_complete = true;
1081        let _m = p.start();
1082        p.bump_any();
1083        no_recover!(p, ts_type(p));
1084        _m.complete(p, TS_DEFAULT);
1085    }
1086    if should_complete {
1087        Some(m.complete(p, TS_TYPE_PARAM))
1088    } else {
1089        m.abandon(p);
1090        let err = p
1091            .err_builder("expected a type parameter, but found none")
1092            .primary(p.cur_tok().range, "");
1093
1094        p.err_recover(
1095            err,
1096            token_set![T![ident], T![yield], T![await], T![>], T![=]],
1097            false,
1098        );
1099        None
1100    }
1101}
1102
1103pub fn ts_import(p: &mut Parser) -> Option<CompletedMarker> {
1104    let m = p.start();
1105    p.expect_no_recover(T![import])?;
1106    p.expect_no_recover(T!['('])?;
1107    p.expect_no_recover(STRING)?;
1108    p.expect_no_recover(T![')'])?;
1109    if p.eat(T![.]) {
1110        ts_entity_name(p, None, false);
1111    }
1112    if p.at(T![<]) && !p.has_linebreak_before_n(0) {
1113        ts_type_args(p);
1114    }
1115
1116    Some(m.complete(p, TS_IMPORT))
1117}
1118
1119pub fn ts_type_query(p: &mut Parser) -> Option<CompletedMarker> {
1120    let m = p.start();
1121    p.expect_no_recover(T![typeof])?;
1122
1123    if p.at(T![import]) {
1124        no_recover!(p, ts_import(p));
1125    } else {
1126        no_recover!(p, ts_entity_name(p, None, true));
1127    }
1128    Some(m.complete(p, TS_TYPE_QUERY))
1129}
1130
1131pub fn ts_mapped_type(p: &mut Parser) -> Option<CompletedMarker> {
1132    let m = p.start();
1133    p.expect_no_recover(T!['{'])?;
1134    let tok = p.cur_tok().range;
1135    let _m = p.start();
1136    if p.eat(T![+]) || p.eat(T![-]) {
1137        if p.cur_src() != "readonly" {
1138            let err = p
1139                .err_builder("`+` and `-` modifiers in mapped types must be followed by `readonly`")
1140                .primary(tok, "");
1141
1142            p.error(err);
1143        } else {
1144            p.bump_remap(T![readonly]);
1145        }
1146        _m.complete(p, TS_MAPPED_TYPE_READONLY);
1147    } else if p.cur_src() == "readonly" {
1148        p.bump_remap(T![readonly]);
1149        _m.complete(p, TS_MAPPED_TYPE_READONLY);
1150    } else {
1151        _m.abandon(p);
1152    }
1153
1154    let param = p.start();
1155    p.expect_no_recover(T!['['])?;
1156    // This is basically to unwrap the marker from a node to a single token
1157    if let Some(x) = identifier_name(p) {
1158        x.undo_completion(p).abandon(p)
1159    }
1160    if p.cur_src() != "in" {
1161        let err = p
1162            .err_builder("expected `in` after a mapped type parameter name")
1163            .primary(p.cur_tok().range, "");
1164
1165        p.error(err);
1166    } else {
1167        p.bump_any();
1168    }
1169    no_recover!(p, ts_type(p));
1170    if p.cur_src() == "as" {
1171        p.bump_any();
1172        ts_type(p);
1173    }
1174    p.expect_no_recover(T![']'])?;
1175    param.complete(p, TS_MAPPED_TYPE_PARAM);
1176    let tok = p.cur_tok().range;
1177    if p.eat(T![+]) || p.eat(T![-]) {
1178        if !p.at(T![?]) {
1179            // TODO: Im not sure of the proper terminology for this, someone should clarify this error
1180            let err = p
1181                .err_builder("`+` and `-` modifiers in mapped types must be followed by `?`")
1182                .primary(tok, "");
1183
1184            p.error(err);
1185        } else {
1186            p.bump_any();
1187        }
1188    } else if p.at(T![?]) {
1189        p.bump_any();
1190    }
1191
1192    p.expect_no_recover(T![:])?;
1193    no_recover!(p, ts_type(p));
1194    // FIXME: This should issue an error for no semi and no ASI, but the fact that a `}` is expected
1195    // after should make this case kind of rare
1196    p.eat(T![;]);
1197    p.expect_no_recover(T!['}'])?;
1198    Some(m.complete(p, TS_MAPPED_TYPE))
1199}
1200
1201fn is_mapped_type_start(p: &Parser) -> bool {
1202    if (p.nth_at(1, T![+]) || p.nth_at(1, T![-])) && p.nth_src(2) == "readonly" {
1203        return true;
1204    }
1205    let mut cur = 1;
1206    if p.cur_src() == "readonly" {
1207        cur += 1;
1208    }
1209    if !p.nth_at(cur, T!['[']) {
1210        return false;
1211    }
1212    cur += 1;
1213    if !matches!(p.nth(cur), T![yield] | T![await] | T![ident]) {
1214        return false;
1215    }
1216    cur += 1;
1217    p.nth_at(cur, T![in])
1218}
1219
1220pub fn ts_predicate(p: &mut Parser) -> Option<CompletedMarker> {
1221    let m = p.start();
1222    let mut advanced = false;
1223
1224    if p.cur_src() == "asserts" {
1225        p.bump_any();
1226        advanced = true;
1227    }
1228
1229    if p.at(T![this]) {
1230        let _m = p.start();
1231        p.bump_any();
1232        _m.complete(p, TS_THIS);
1233        advanced = true;
1234    } else if p.at_ts(token_set![T![await], T![yield], T![ident]]) {
1235        let _m = p.start();
1236        p.bump_any();
1237        _m.complete(p, TS_TYPE_NAME);
1238        advanced = true;
1239    }
1240
1241    if p.cur_src() == "is" {
1242        p.bump_any();
1243        no_recover!(p, ts_type(p));
1244        advanced = true;
1245    }
1246
1247    if !advanced {
1248        m.abandon(p);
1249        None
1250    } else {
1251        Some(m.complete(p, TS_PREDICATE))
1252    }
1253}
1254
1255pub(crate) fn maybe_eat_incorrect_modifier(p: &mut Parser) -> Option<CompletedMarker> {
1256    let maybe_err = p.start();
1257    if matches!(p.cur_src(), "public" | "private" | "protected") {
1258        let m = p.start();
1259        p.bump_any();
1260        Some(m.complete(p, ERROR))
1261    } else if ts_modifier(p, &["readonly"]).is_some() {
1262        Some(maybe_err.complete(p, ERROR))
1263    } else {
1264        None
1265    }
1266}
1267
1268pub fn ts_type_ref(
1269    p: &mut Parser,
1270    recovery_set: impl Into<Option<TokenSet>> + Clone,
1271) -> Option<CompletedMarker> {
1272    let m = p.start();
1273    if let Some(err_m) = maybe_eat_incorrect_modifier(p) {
1274        let err = p
1275            .err_builder("a parameter property is only allowed in a constructor implementation")
1276            .primary(err_m.range(p), "");
1277
1278        p.error(err);
1279    }
1280
1281    ts_entity_name(p, recovery_set, true)?;
1282    if !p.has_linebreak_before_n(0) && p.at(T![<]) {
1283        no_recover!(p, ts_type_args(p));
1284    }
1285
1286    Some(m.complete(p, TS_TYPE_REF))
1287}
1288
1289pub fn ts_entity_name(
1290    p: &mut Parser,
1291    recovery_set: impl Into<Option<TokenSet>> + Clone,
1292    allow_reserved: bool,
1293) -> Option<CompletedMarker> {
1294    let init = ts_type_name(p, recovery_set.clone(), false)?;
1295    // TODO: maybe we should recover if no init at this point?
1296
1297    let mut lhs = init;
1298    let set = recovery_set
1299        .into()
1300        .unwrap_or(BASE_TS_RECOVERY_SET)
1301        .union(token_set![T![.]]);
1302
1303    while p.at(T![.]) {
1304        let m = lhs.precede(p);
1305        p.bump_any();
1306        // TODO: we should maybe move recovery out of ts_type_name since we dont need recovery here
1307        no_recover!(p, ts_type_name(p, set, allow_reserved));
1308        lhs = m.complete(p, TS_QUALIFIED_PATH);
1309    }
1310    Some(lhs)
1311}
1312
1313pub fn ts_type_name(
1314    p: &mut Parser,
1315    recovery_set: impl Into<Option<TokenSet>>,
1316    allow_reserved: bool,
1317) -> Option<CompletedMarker> {
1318    if p.at(T![ident]) || (p.cur().is_keyword() && allow_reserved) {
1319        let m = p.start();
1320        p.bump_remap(T![ident]);
1321        return Some(m.complete(p, TS_TYPE_NAME));
1322    }
1323
1324    // FIXME: move the recovery job out of this method
1325    let set = recovery_set.into().unwrap_or(BASE_TS_RECOVERY_SET);
1326    let err = p
1327        .err_builder(&format!(
1328            "expected a TypeScript type name, but instead found `{}`",
1329            p.cur_src()
1330        ))
1331        .primary(p.cur_tok().range, "");
1332
1333    p.err_recover(err, set, false)?;
1334    None
1335}