swc_ecma_lexer/common/parser/
ident.rs

1use either::Either;
2use swc_atoms::atom;
3use swc_common::BytePos;
4use swc_ecma_ast::*;
5
6use super::{buffer::Buffer, expr::parse_str_lit, PResult, Parser};
7use crate::{
8    common::{context::Context, lexer::token::TokenFactory, parser::token_and_span::TokenAndSpan},
9    error::SyntaxError,
10};
11
12// https://tc39.es/ecma262/#prod-ModuleExportName
13pub fn parse_module_export_name<'a, P: Parser<'a>>(p: &mut P) -> PResult<ModuleExportName> {
14    let cur = p.input().cur();
15    let module_export_name = if cur.is_str() {
16        ModuleExportName::Str(parse_str_lit(p))
17    } else if cur.is_word() {
18        ModuleExportName::Ident(parse_ident_name(p)?.into())
19    } else {
20        unexpected!(p, "identifier or string");
21    };
22    Ok(module_export_name)
23}
24
25/// Use this when spec says "IdentifierName".
26/// This allows idents like `catch`.
27pub fn parse_ident_name<'a, P: Parser<'a>>(p: &mut P) -> PResult<IdentName> {
28    let token_and_span = p.input().get_cur();
29    let start = token_and_span.span().lo;
30    let cur = token_and_span.token();
31    let w = if cur.is_word() {
32        p.input_mut().expect_word_token_and_bump()
33    } else if cur.is_jsx_name() && p.ctx().contains(Context::InType) {
34        p.input_mut().expect_jsx_name_token_and_bump()
35    } else {
36        syntax_error!(p, SyntaxError::ExpectedIdent)
37    };
38    Ok(IdentName::new(w, p.span(start)))
39}
40
41pub fn parse_maybe_private_name<'a, P: Parser<'a>>(
42    p: &mut P,
43) -> PResult<Either<PrivateName, IdentName>> {
44    let is_private = p.input().is(&P::Token::HASH);
45    if is_private {
46        parse_private_name(p).map(Either::Left)
47    } else {
48        parse_ident_name(p).map(Either::Right)
49    }
50}
51
52pub fn parse_private_name<'a, P: Parser<'a>>(p: &mut P) -> PResult<PrivateName> {
53    let start = p.cur_pos();
54    p.assert_and_bump(&P::Token::HASH);
55    let hash_end = p.input().prev_span().hi;
56    if p.input().cur_pos() - hash_end != BytePos(0) {
57        syntax_error!(p, p.span(start), SyntaxError::SpaceBetweenHashAndIdent);
58    }
59    let id = parse_ident_name(p)?;
60    Ok(PrivateName {
61        span: p.span(start),
62        name: id.sym,
63    })
64}
65
66/// IdentifierReference
67#[inline]
68pub fn parse_ident_ref<'a>(p: &mut impl Parser<'a>) -> PResult<Ident> {
69    let ctx = p.ctx();
70    parse_ident(
71        p,
72        !ctx.contains(Context::InGenerator),
73        !ctx.contains(Context::InAsync),
74    )
75}
76
77/// LabelIdentifier
78#[inline]
79pub fn parse_label_ident<'a>(p: &mut impl Parser<'a>) -> PResult<Ident> {
80    parse_ident_ref(p)
81}
82
83/// babel: `parseBindingIdentifier`
84///
85/// spec: `BindingIdentifier`
86pub fn parse_binding_ident<'a>(
87    p: &mut impl Parser<'a>,
88    disallow_let: bool,
89) -> PResult<BindingIdent> {
90    trace_cur!(p, parse_binding_ident);
91
92    let cur = p.input().cur();
93    if disallow_let && cur.is_let() {
94        unexpected!(p, "let is reserved in const, let, class declaration")
95    } else if cur.is_unknown_ident() {
96        let span = p.input().cur_span();
97        let word = p.input_mut().expect_word_token_and_bump();
98        if atom!("arguments") == word || atom!("eval") == word {
99            p.emit_strict_mode_err(span, SyntaxError::EvalAndArgumentsInStrict);
100        }
101        return Ok(Ident::new_no_ctxt(word, span).into());
102    }
103
104    // "yield" and "await" is **lexically** accepted.
105    let ident = parse_ident(p, true, true)?;
106    let ctx = p.ctx();
107    if (ctx.intersects(Context::InAsync.union(Context::InStaticBlock)) && ident.sym == "await")
108        || (ctx.contains(Context::InGenerator) && ident.sym == "yield")
109    {
110        p.emit_err(ident.span, SyntaxError::ExpectedIdent);
111    }
112
113    Ok(ident.into())
114}
115
116pub fn parse_opt_binding_ident<'a>(
117    p: &mut impl Parser<'a>,
118    disallow_let: bool,
119) -> PResult<Option<BindingIdent>> {
120    trace_cur!(p, parse_opt_binding_ident);
121    let token_and_span = p.input().get_cur();
122    let cur = token_and_span.token();
123    if cur.is_this() && p.input().syntax().typescript() {
124        let start = token_and_span.span().lo;
125        Ok(Some(
126            Ident::new_no_ctxt(atom!("this"), p.span(start)).into(),
127        ))
128    } else if cur.is_word() && !cur.is_reserved(p.ctx()) {
129        parse_binding_ident(p, disallow_let).map(Some)
130    } else {
131        Ok(None)
132    }
133}
134
135/// Identifier
136///
137/// In strict mode, "yield" is SyntaxError if matched.
138pub fn parse_ident<'a>(
139    p: &mut impl Parser<'a>,
140    incl_yield: bool,
141    incl_await: bool,
142) -> PResult<Ident> {
143    trace_cur!(p, parse_ident);
144
145    let token_and_span = p.input().get_cur();
146    if !token_and_span.token().is_word() {
147        syntax_error!(p, SyntaxError::ExpectedIdent)
148    }
149    let span = token_and_span.span();
150    let start = span.lo;
151    let t = token_and_span.token();
152
153    // Spec:
154    // It is a Syntax Error if this phrase is contained in strict mode code and the
155    // StringValue of IdentifierName is: "implements", "interface", "let",
156    // "package", "private", "protected", "public", "static", or "yield".
157    if t.is_enum() {
158        let word = p.input_mut().expect_word_token_and_bump();
159        p.emit_err(span, SyntaxError::InvalidIdentInStrict(word.clone()));
160        return Ok(Ident::new_no_ctxt(word, p.span(start)));
161    } else if t.is_yield()
162        || t.is_let()
163        || t.is_static()
164        || t.is_implements()
165        || t.is_interface()
166        || t.is_package()
167        || t.is_private()
168        || t.is_protected()
169        || t.is_public()
170    {
171        let word = p.input_mut().expect_word_token_and_bump();
172        p.emit_strict_mode_err(span, SyntaxError::InvalidIdentInStrict(word.clone()));
173        return Ok(Ident::new_no_ctxt(word, p.span(start)));
174    };
175
176    let word;
177
178    // Spec:
179    // It is a Syntax Error if StringValue of IdentifierName is the same String
180    // value as the StringValue of any ReservedWord except for yield or await.
181    if t.is_await() {
182        let ctx = p.ctx();
183        if ctx.contains(Context::InDeclare) {
184            word = atom!("await");
185        } else if ctx.contains(Context::InStaticBlock) {
186            syntax_error!(p, span, SyntaxError::ExpectedIdent)
187        } else if ctx.contains(Context::Module) | ctx.contains(Context::InAsync) {
188            syntax_error!(p, span, SyntaxError::InvalidIdentInAsync)
189        } else if incl_await {
190            word = atom!("await")
191        } else {
192            syntax_error!(p, span, SyntaxError::ExpectedIdent)
193        }
194    } else if t.is_this() && p.input().syntax().typescript() {
195        word = atom!("this")
196    } else if t.is_let() {
197        word = atom!("let")
198    } else if t.is_known_ident() {
199        let ident = t.take_known_ident();
200        word = ident
201    } else if t.is_unknown_ident() {
202        let word = p.input_mut().expect_word_token_and_bump();
203        if p.ctx().contains(Context::InClassField) && word == atom!("arguments") {
204            p.emit_err(span, SyntaxError::ArgumentsInClassField)
205        }
206        return Ok(Ident::new_no_ctxt(word, p.span(start)));
207    } else if t.is_yield() && incl_yield {
208        word = atom!("yield")
209    } else if t.is_null() || t.is_true() || t.is_false() || t.is_keyword() {
210        syntax_error!(p, span, SyntaxError::ExpectedIdent)
211    } else {
212        unreachable!()
213    }
214    p.bump();
215
216    Ok(Ident::new_no_ctxt(word, p.span(start)))
217}