use syntax::decl::is_semi;
use super::decl::{
arrow_body, class_decl, formal_parameters, function_decl, maybe_private_name, method,
};
use super::pat::pattern;
use super::typescript::*;
use super::util::*;
use crate::{SyntaxKind::*, *};
pub const LITERAL: TokenSet = token_set![TRUE_KW, FALSE_KW, NUMBER, STRING, NULL_KW, REGEX];
pub const EXPR_RECOVERY_SET: TokenSet = token_set![VAR_KW, R_PAREN, L_PAREN, L_BRACK, R_BRACK];
pub const ASSIGN_TOKENS: TokenSet = token_set![
T![=],
T![+=],
T![-=],
T![*=],
T![%=],
T![<<=],
T![>>=],
T![>>>=],
T![&=],
T![|=],
T![^=],
T![&&=],
T![||=],
T![??=],
T![/=],
T![>>=]
];
pub const STARTS_EXPR: TokenSet = token_set![
T![!],
T!['('],
T!['['],
T!['{'],
T![++],
T![--],
T![~],
T![+],
T![-],
T![throw],
T![new],
T![typeof],
T![void],
T![delete],
T![ident],
T![...],
T![this],
T![yield],
T![await],
T![function],
T![class],
T![import],
T![super],
BACKTICK,
]
.union(LITERAL);
pub fn literal(p: &mut Parser) -> Option<CompletedMarker> {
if !p.at_ts(LITERAL) {
return None;
}
let m = p.start();
p.bump_any();
Some(m.complete(p, SyntaxKind::LITERAL))
}
pub fn assign_expr(p: &mut Parser) -> Option<CompletedMarker> {
if p.at(T![<])
&& (token_set![T![ident], T![await], T![yield]].contains(p.nth(1)) || p.nth(1).is_keyword())
{
let res = try_parse_ts(p, |p| {
let m = p.start();
ts_type_params(p)?;
let res = assign_expr_base(p);
if res.map(|x| x.kind()) != Some(ARROW_EXPR) {
m.abandon(p);
None
} else {
res.unwrap().undo_completion(p).abandon(p);
Some(m.complete(p, ARROW_EXPR))
}
});
if let Some(mut res) = res {
res.err_if_not_ts(p, "type parameters can only be used in TypeScript files");
return Some(res);
}
}
assign_expr_base(p)
}
fn assign_expr_base(p: &mut Parser) -> Option<CompletedMarker> {
if p.state.in_generator && p.at(T![yield]) {
return Some(yield_expr(p));
}
let potential_arrow_start = matches!(p.cur(), T![ident] | T!['('] | T![yield] | T![await]);
let mut guard = p.with_state(ParserState {
potential_arrow_start,
..p.state.clone()
});
let checkpoint = guard.checkpoint();
let target = conditional_expr(&mut *guard)?;
assign_expr_recursive(&mut *guard, target, checkpoint)
}
pub(crate) fn is_valid_target(p: &mut Parser, marker: &CompletedMarker) -> bool {
match marker.kind() {
DOT_EXPR | BRACKET_EXPR | NAME_REF | PRIVATE_PROP_ACCESS | TS_CONST_ASSERTION
| TS_ASSERTION | TS_NON_NULL => true,
GROUPING_EXPR => {
for (idx, event) in p.events[marker.start_pos as usize..].iter().enumerate() {
match event {
Event::Finish { .. } if marker.finish_pos as usize == idx => return true,
Event::Start {
kind: SyntaxKind::GROUPING_EXPR,
..
} => {}
Event::Start {
kind: SyntaxKind::TOMBSTONE,
..
} => {}
Event::Start { kind, .. } => {
return matches!(
kind,
DOT_EXPR
| BRACKET_EXPR
| NAME_REF
| PRIVATE_PROP_ACCESS
| TS_CONST_ASSERTION
| TS_ASSERTION
| TS_NON_NULL
);
}
_ => {}
}
}
true
}
_ => false,
}
}
fn check_assign_target_from_marker(p: &mut Parser, marker: &CompletedMarker) {
if !is_valid_target(p, marker) {
let err = p
.err_builder(&format!(
"Invalid assignment to `{}`",
p.source(marker.range(p))
))
.primary(marker.range(p), "This expression cannot be assigned to");
p.error(err);
}
}
fn assign_expr_recursive(
p: &mut Parser,
mut target: CompletedMarker,
checkpoint: Checkpoint,
) -> Option<CompletedMarker> {
if p.at_ts(ASSIGN_TOKENS) {
if p.at(T![=]) {
if !is_valid_target(p, &target) && target.kind() != TEMPLATE {
p.rewind(checkpoint);
target = pattern(p, false, true)?;
}
} else {
if !is_valid_target(p, &target) {
let err = p
.err_builder(&format!(
"Invalid assignment to `{}`",
p.source(target.range(p)).trim()
))
.primary(target.range(p), "This expression cannot be assigned to");
p.error(err);
}
let text = p.source(target.range(p));
if (text == "eval" || text == "arguments") && p.state.strict.is_some() && p.typescript()
{
let err = p
.err_builder("`eval` and `arguments` cannot be assigned to")
.primary(target.range(p), "");
p.error(err);
}
}
let m = target.precede(p);
p.bump_any();
assign_expr(p);
Some(m.complete(p, ASSIGN_EXPR))
} else {
Some(target)
}
}
pub fn yield_expr(p: &mut Parser) -> CompletedMarker {
let m = p.start();
p.expect(T![yield]);
if !is_semi(p, 0) && (p.at(T![*]) || p.at_ts(STARTS_EXPR)) {
p.eat(T![*]);
assign_expr(p);
}
m.complete(p, YIELD_EXPR)
}
pub fn conditional_expr(p: &mut Parser) -> Option<CompletedMarker> {
let lhs = binary_expr(p);
if p.at(T![?]) {
let m = lhs?.precede(p);
p.bump_any();
assign_expr(&mut *p.with_state(ParserState {
in_cond_expr: true,
..p.state.clone()
}));
p.expect(T![:]);
assign_expr(p);
return Some(m.complete(p, COND_EXPR));
}
lhs
}
pub fn binary_expr(p: &mut Parser) -> Option<CompletedMarker> {
let left = unary_expr(p);
binary_expr_recursive(p, left, 0)
}
fn binary_expr_recursive(
p: &mut Parser,
left: Option<CompletedMarker>,
min_prec: u8,
) -> Option<CompletedMarker> {
if 7 > min_prec && !p.has_linebreak_before_n(0) && p.cur_src() == "as" {
let m = left.map(|x| x.precede(p)).unwrap_or_else(|| p.start());
p.bump_any();
let mut res = if p.eat(T![const]) {
m.complete(p, TS_CONST_ASSERTION)
} else {
ts_type(p);
m.complete(p, TS_ASSERTION)
};
res.err_if_not_ts(p, "type assertions can only be used in TypeScript files");
return binary_expr_recursive(p, Some(res), min_prec);
}
let kind = match p.cur() {
T![>] if p.nth_at(1, T![>]) && p.nth_at(2, T![>]) => T![>>>],
T![>] if p.nth_at(1, T![>]) => T![>>],
k => k,
};
let precedence = match kind {
T![in] if p.state.include_in => 7,
T![instanceof] => 7,
_ => {
if let Some(prec) = get_precedence(kind) {
prec
} else {
return left;
}
}
};
if precedence <= min_prec {
return left;
}
let op = kind;
let op_tok = p.cur_tok();
let m = left.map(|m| m.precede(p)).unwrap_or_else(|| p.start());
if op == T![>>] {
p.bump_multiple(2, T![>>]);
} else if op == T![>>>] {
p.bump_multiple(3, T![>>>]);
} else {
p.bump_any();
}
let right = if get_precedence(p.cur()).is_some() && !p.at_ts(token_set![T![-], T![+], T![<]]) {
let err = p.err_builder(&format!("Expected an expression for the right hand side of a `{}`, but found an operator instead", p.token_src(&op_tok)))
.secondary(op_tok.range, "This operator requires a right hand side value")
.primary(p.cur_tok().range, "But this operator was encountered instead");
p.error(err);
None
} else {
unary_expr(p)
};
binary_expr_recursive(
p,
right,
if op == T![**] {
precedence - 1
} else {
precedence
},
);
let complete = m.complete(p, BIN_EXPR);
binary_expr_recursive(p, Some(complete), min_prec)
}
pub fn paren_expr(p: &mut Parser) -> CompletedMarker {
let m = p.start();
p.expect(T!['(']);
expr(p);
p.expect(T![')']);
m.complete(p, GROUPING_EXPR)
}
pub fn member_or_new_expr(p: &mut Parser, new_expr: bool) -> Option<CompletedMarker> {
if p.at(T![new]) {
let m = p.start();
p.bump_any();
if p.at(T![.]) && p.token_src(&p.nth_tok(1)) == "target" {
p.bump_any();
p.bump_any();
let complete = m.complete(p, NEW_TARGET);
return Some(subscripts(p, complete, true));
}
let complete = member_or_new_expr(p, new_expr)?;
if complete.kind() == ARROW_EXPR {
return Some(complete);
}
if p.at(T![<]) {
if let Some(mut complete) = try_parse_ts(p, |p| {
let compl = ts_type_args(p);
if !p.at(T!['(']) {
return None;
}
compl
}) {
complete.err_if_not_ts(
p,
"`new` expressions can only have type arguments in TypeScript files",
);
}
}
if !new_expr || p.at(T!['(']) {
args(p);
let complete = m.complete(p, NEW_EXPR);
return Some(subscripts(p, complete, true));
}
return Some(m.complete(p, NEW_EXPR));
}
if p.at(T![super]) && token_set!(T![.], T!['[']).contains(p.nth(1)) {
let m = p.start();
p.bump_any();
let lhs = match p.cur() {
T![.] => {
p.bump_any();
identifier_name(p);
m.complete(p, DOT_EXPR)
}
T!['['] => {
p.bump_any();
expr(p);
p.expect(T![']']);
m.complete(p, BRACKET_EXPR)
}
_ => unreachable!(),
};
return Some(subscripts(p, lhs, true));
}
let lhs = primary_expr(p)?;
Some(subscripts(p, lhs, true))
}
pub fn subscripts(p: &mut Parser, mut lhs: CompletedMarker, no_call: bool) -> CompletedMarker {
let mut should_try_parsing_ts = true;
while !p.at(EOF) {
match p.cur() {
T![?.] if p.nth_at(1, T!['(']) => {
lhs = {
let m = lhs.precede(p);
p.bump_any();
args(p);
m.complete(p, CALL_EXPR)
}
}
T!['('] if !no_call => {
lhs = {
let m = lhs.precede(p);
args(p);
m.complete(p, CALL_EXPR)
}
}
T![?.] if p.nth_at(1, T!['[']) => lhs = bracket_expr(p, lhs, true),
T!['['] => lhs = bracket_expr(p, lhs, false),
T![?.] => lhs = dot_expr(p, lhs, true),
T![.] => lhs = dot_expr(p, lhs, false),
T![!] if !p.has_linebreak_before_n(0) => {
lhs = {
let m = lhs.precede(p);
p.bump_any();
let mut comp = m.complete(p, TS_NON_NULL);
comp.err_if_not_ts(
p,
"non-null assertions can only be used in TypeScript files",
);
comp
}
}
T![<] if p.typescript() && should_try_parsing_ts => {
let res = try_parse_ts(p, |p| {
let m = lhs.precede(p);
ts_type_args(p)?;
if !no_call && p.at(T!['(']) {
args(p);
Some(m.complete(p, CALL_EXPR))
} else if p.at(BACKTICK) {
m.abandon(p);
Some(template(p, Some(lhs)))
} else {
None
}
});
if res.is_none() {
should_try_parsing_ts = false;
}
}
BACKTICK => lhs = template(p, Some(lhs)),
_ => return lhs,
}
}
lhs
}
pub fn dot_expr(p: &mut Parser, lhs: CompletedMarker, optional_chain: bool) -> CompletedMarker {
let m = lhs.precede(p);
let range = if optional_chain {
Some(p.cur_tok().range).filter(|_| p.expect(T![?.]))
} else {
p.expect(T![.]);
None
};
if let Some(priv_range) = maybe_private_name(p).filter(|x| x.kind() == PRIVATE_NAME) {
if !p.syntax.class_fields {
let err = p
.err_builder("private identifiers are unsupported")
.primary(priv_range.range(p), "");
p.error(err);
return m.complete(p, ERROR);
}
if let Some(range) = range {
let err = p
.err_builder("optional chaining cannot contain private identifiers")
.primary(range, "");
p.error(err);
m.complete(p, ERROR)
} else {
m.complete(p, PRIVATE_PROP_ACCESS)
}
} else {
m.complete(p, DOT_EXPR)
}
}
pub fn bracket_expr(p: &mut Parser, lhs: CompletedMarker, optional_chain: bool) -> CompletedMarker {
let m = lhs.precede(p);
if optional_chain {
p.expect(T![?.]);
}
p.expect(T!['[']);
expr(p);
p.expect(T![']']);
m.complete(p, BRACKET_EXPR)
}
pub fn identifier_name(p: &mut Parser) -> Option<CompletedMarker> {
let m = p.start();
match p.cur() {
t if t.is_keyword() || t == T![ident] => p.bump_remap(T![ident]),
_ => {
let err = p
.err_builder("Expected an identifier or keyword")
.primary(p.cur_tok().range, "Expected an identifier or keyword here");
p.error(err);
m.abandon(p);
return None;
}
}
Some(m.complete(p, NAME))
}
pub fn args(p: &mut Parser) -> CompletedMarker {
let m = p.start();
p.expect(T!['(']);
while !p.at(EOF) && !p.at(T![')']) {
if p.at(T![...]) {
spread_element(p);
} else {
assign_expr(p);
}
if p.at(T![,]) {
p.bump_any();
} else {
break;
}
}
p.expect(T![')']);
m.complete(p, ARG_LIST)
}
pub fn paren_or_arrow_expr(p: &mut Parser, can_be_arrow: bool) -> CompletedMarker {
let m = p.start();
let checkpoint = p.checkpoint();
let start = p.cur_tok().range.start;
p.expect(T!['(']);
let mut spread_range = None;
let mut trailing_comma_marker = None;
let mut had_comma = false;
let mut params_marker = None;
let mut temp = p.with_state(ParserState {
potential_arrow_start: true,
..p.state.clone()
});
let expr_m = temp.start();
let mut is_empty = false;
if temp.eat(T![')']) {
is_empty = true;
} else {
loop {
if temp.at(T![...]) {
let m = temp.start();
temp.bump_any();
pattern(&mut *temp, false, false);
if temp.eat(T![:]) {
if let Some(mut ty) = ts_type(&mut *temp) {
ty.err_if_not_ts(
&mut *temp,
"spread elements can only have type annotations in TypeScript files",
);
}
}
let complete = m.complete(&mut *temp, REST_PATTERN);
spread_range = Some(complete.range(&*temp));
if !temp.eat(T![')']) {
if temp.eat(T![=]) {
assign_expr(&mut *temp);
temp.expect(T![')']);
} else {
let err = temp.err_builder(&format!("expect a closing parenthesis after a spread element, but instead found `{}`", temp.cur_src()))
.primary(temp.cur_tok().range, "");
temp.err_recover(err, EXPR_RECOVERY_SET, false);
}
}
break;
}
let expr = assign_expr(&mut *temp);
if expr.is_some() && temp.at(T![:]) {
temp.rewind(checkpoint);
params_marker = Some(formal_parameters(&mut *temp));
break;
}
let sub_m = temp.start();
if temp.eat(T![,]) {
if temp.at(T![')']) {
trailing_comma_marker = Some(sub_m.complete(&mut *temp, ERROR));
temp.bump_any();
break;
} else {
sub_m.abandon(&mut *temp);
}
had_comma = true;
} else {
sub_m.abandon(&mut *temp);
if had_comma {
expr_m.complete(&mut *temp, SEQUENCE_EXPR);
}
temp.expect(T![')']);
break;
}
}
}
drop(temp);
if p.state.in_cond_expr && p.at(T![:]) && params_marker.is_none() {
let func = |p: &mut Parser| {
let p = &mut *p.with_state(ParserState {
no_recovery: true,
..p.state.clone()
});
p.rewind(checkpoint);
formal_parameters(p);
if p.at(T![:]) {
if let Some(mut ret) = ts_type_or_type_predicate_ann(p, T![:]) {
ret.err_if_not_ts(
p,
"arrow functions can only have return types in TypeScript files",
);
}
}
p.expect_no_recover(T![=>])?;
arrow_body(p)?;
Some(())
};
let cloned = p.clone();
if func(p).is_some() {
let c = m.complete(p, ARROW_EXPR);
return c;
} else {
*p = cloned;
}
}
let has_ret_type = !p.state.in_cond_expr && p.at(T![:]) && !p.state.in_case_cond;
if (p.at(T![=>]) && !p.has_linebreak_before_n(0)) || has_ret_type || params_marker.is_some() {
if !can_be_arrow && !p.at(T![:]) {
let err = p
.err_builder("Unexpected token `=>`")
.primary(p.cur_tok().range, "an arrow expression is not allowed here");
p.error(err);
} else {
if params_marker.is_none() {
p.rewind(checkpoint);
formal_parameters(p);
}
if p.at(T![:]) {
let complete = ts_type_or_type_predicate_ann(p, T![:]);
if let Some(mut complete) = complete {
complete.err_if_not_ts(
p,
"arrow functions can only have return types in TypeScript files",
);
}
}
p.bump_any();
arrow_body(p);
return m.complete(p, ARROW_EXPR);
}
}
if let Some(params) = params_marker {
let err = p
.err_builder("grouping expressions cannot contain parameters")
.primary(params.range(p), "");
p.error(err);
return m.complete(p, ERROR);
}
if is_empty {
let err = p
.err_builder("grouping expressions cannot be empty")
.primary(start..p.cur_tok().range.start, "");
p.error(err);
return m.complete(p, GROUPING_EXPR);
}
if let Some(range) = spread_range {
let err = p
.err_builder("Illegal spread element inside grouping expression")
.primary(range, "");
p.error(err);
}
if let Some(complete) = trailing_comma_marker {
let err = p
.err_builder("Illegal trailing comma in grouping expression")
.primary(complete.range(p), "");
p.error(err);
}
m.complete(p, GROUPING_EXPR)
}
pub fn expr_or_spread(p: &mut Parser) -> Option<CompletedMarker> {
if p.at(T![...]) {
let m = p.start();
p.bump_any();
assign_expr(p);
Some(m.complete(p, SPREAD_ELEMENT))
} else {
assign_expr(p)
}
}
pub fn expr(p: &mut Parser) -> Option<CompletedMarker> {
let first = assign_expr(p)?;
if p.at(T![,]) {
let m = first.precede(p);
p.bump_any();
assign_expr(p)?;
while p.at(T![,]) {
p.bump_any();
assign_expr(p)?;
}
return Some(m.complete(p, SEQUENCE_EXPR));
}
Some(first)
}
pub fn primary_expr(p: &mut Parser) -> Option<CompletedMarker> {
if let Some(m) = literal(p) {
return Some(m);
}
let complete = match p.cur() {
T![this] => {
let m = p.start();
p.bump_any();
m.complete(p, THIS_EXPR)
}
T![class] => {
let mut m = class_decl(p, true);
m.change_kind(p, CLASS_EXPR);
m
}
T![ident] if p.cur_src() == "async" => {
if p.nth_at(1, T![function]) {
let m = p.start();
p.bump_remap(T![async]);
let mut complete = function_decl(
&mut *p.with_state(ParserState {
in_async: true,
..p.state.clone()
}),
m,
true,
);
complete.change_kind(p, FN_EXPR);
complete
} else {
if p.state.potential_arrow_start
&& token_set![T![ident], T![yield], T!['(']].contains(p.nth(1))
{
let m = p.start();
p.bump_remap(T![async]);
if p.at(T!['(']) {
formal_parameters(p);
} else {
let m = p.start();
p.bump_remap(T![ident]);
m.complete(p, NAME);
}
if p.at(T![:]) {
let complete = ts_type_or_type_predicate_ann(p, T![:]);
if let Some(mut complete) = complete {
complete.err_if_not_ts(
p,
"arrow functions can only have return types in TypeScript files",
);
}
}
p.expect(T![=>]);
arrow_body(&mut *p.with_state(ParserState {
in_async: true,
..p.state.clone()
}));
m.complete(p, ARROW_EXPR)
} else {
identifier_reference(p)?
}
}
}
T![function] => {
let m = p.start();
let mut complete = function_decl(p, m, true);
complete.change_kind(p, FN_EXPR);
complete
}
T![ident] | T![yield] | T![await] => {
let mut ident = identifier_reference(p)?;
if p.state.potential_arrow_start && p.at(T![=>]) && !p.has_linebreak_before_n(0) {
ident.change_kind(p, NAME);
let m = ident.precede(p);
p.bump_any();
arrow_body(p);
m.complete(p, ARROW_EXPR)
} else {
ident
}
}
T!['('] => paren_or_arrow_expr(p, p.state.potential_arrow_start),
T!['['] => array_expr(p),
T!['{'] if p.state.allow_object_expr => object_expr(p),
T![import] => {
let m = p.start();
p.bump_any();
if p.eat(T![.]) {
if p.at(T![ident]) && p.token_src(&p.cur_tok()) == "meta" {
p.bump_any();
m.complete(p, IMPORT_META)
} else if p.at(T![ident]) {
let err = p
.err_builder(&format!(
"Expected `meta` following an import keyword, but found `{}`",
p.token_src(&p.cur_tok())
))
.primary(p.cur_tok().range, "");
p.err_and_bump(err);
m.complete(p, ERROR)
} else {
let err = p
.err_builder("Expected `meta` following an import keyword, but found none")
.primary(p.cur_tok().range, "");
p.error(err);
m.complete(p, ERROR)
}
} else {
p.expect(T!['(']);
assign_expr(p);
p.expect(T![')']);
m.complete(p, IMPORT_CALL)
}
}
BACKTICK => template(p, None),
ERROR_TOKEN => {
let m = p.start();
p.bump_any();
m.complete(p, ERROR)
}
_ => {
let err = p
.err_builder("Expected an expression, but found none")
.primary(p.cur_tok().range, "Expected an expression here");
p.err_recover(err, p.state.expr_recovery_set, true);
return None;
}
};
Some(complete)
}
pub fn identifier_reference(p: &mut Parser) -> Option<CompletedMarker> {
match p.cur() {
T![ident] | T![yield] | T![await] => {
let m = p.start();
p.bump_remap(T![ident]);
Some(m.complete(p, NAME_REF))
}
_ => {
let err = p
.err_builder("Expected an identifier, but found none")
.primary(p.cur_tok().range, "");
p.err_recover(err, p.state.expr_recovery_set, true);
None
}
}
}
pub fn template(p: &mut Parser, tag: Option<CompletedMarker>) -> CompletedMarker {
let m = tag.map(|m| m.precede(p)).unwrap_or_else(|| p.start());
p.expect(BACKTICK);
while !p.at(EOF) && !p.at(BACKTICK) {
match p.cur() {
TEMPLATE_CHUNK => p.bump_any(),
DOLLARCURLY => {
let e = p.start();
p.bump_any();
expr(p);
p.expect(T!['}']);
e.complete(p, TEMPLATE_ELEMENT);
},
t => unreachable!("Anything not template chunk or dollarcurly should have been eaten by the lexer, but {:?} was found", t),
}
}
p.eat(BACKTICK);
m.complete(p, TEMPLATE)
}
pub fn array_expr(p: &mut Parser) -> CompletedMarker {
let m = p.start();
p.expect(T!['[']);
while !p.at(EOF) {
while p.eat(T![,]) {}
if p.at(T![']']) {
break;
}
if p.at(T![...]) {
spread_element(p);
} else {
assign_expr(p);
}
if p.at(T![']']) {
break;
}
p.expect(T![,]);
}
p.expect(T![']']);
m.complete(p, ARRAY_EXPR)
}
pub fn spread_element(p: &mut Parser) -> CompletedMarker {
let m = p.start();
p.expect(T![...]);
assign_expr(p);
m.complete(p, SPREAD_ELEMENT)
}
pub fn object_expr(p: &mut Parser) -> CompletedMarker {
let m = p.start();
p.expect(T!['{']);
let mut first = true;
while !p.at(EOF) && !p.at(T!['}']) {
if first {
first = false;
} else {
p.expect(T![,]);
if p.at(T!['}']) {
break;
}
}
object_property(p);
}
p.expect(T!['}']);
m.complete(p, OBJECT_EXPR)
}
const STARTS_OBJ_PROP: TokenSet =
token_set![STRING, NUMBER, T![ident], T![await], T![yield], T!['[']];
pub fn object_property(p: &mut Parser) -> Option<CompletedMarker> {
let m = p.start();
match p.cur() {
T![ident]
if (p.cur_src() == "get" || p.cur_src() == "set")
&& (p.nth_at(1, T![ident]) || p.nth_at(1, T!['['])) =>
{
method(p, None, None)
}
T![ident]
if p.cur_src() == "async"
&& !p.has_linebreak_before_n(1)
&& (STARTS_OBJ_PROP.contains(p.nth(1)) || p.nth_at(1, T![*])) =>
{
method(p, None, None)
}
T![...] => {
p.bump_any();
assign_expr(p);
Some(m.complete(p, SPREAD_PROP))
}
T![*] => {
let m = p.start();
method(p, m, None)
}
_ => {
let prop = object_prop_name(p, false);
if let Some(NAME) = prop.map(|m| m.kind()) {
if p.eat(T![=]) {
assign_expr(p);
return Some(m.complete(p, INITIALIZED_PROP));
}
}
if p.at(T!['(']) || p.at(T![<]) {
method(p, m, None)
} else {
if prop.is_none() {
p.err_recover_no_err(token_set![T![:], T![,]], false);
}
if p.at(T![:]) || prop?.kind() != NAME {
p.expect(T![:]);
assign_expr(p);
Some(m.complete(p, LITERAL_PROP))
} else {
Some(m.complete(p, IDENT_PROP))
}
}
}
}
}
pub fn object_prop_name(p: &mut Parser, binding: bool) -> Option<CompletedMarker> {
match p.cur() {
STRING | NUMBER => literal(p),
T!['['] => {
let m = p.start();
p.bump_any();
assign_expr(p);
p.expect(T![']']);
Some(m.complete(p, COMPUTED_PROPERTY_NAME))
}
_ if binding => super::pat::binding_identifier(p),
_ => identifier_name(p),
}
}
pub fn lhs_expr(p: &mut Parser) -> Option<CompletedMarker> {
if p.at(T![super]) && p.nth_at(1, T!['(']) {
let m = p.start();
p.bump_any();
args(p);
let lhs = m.complete(p, SUPER_CALL);
return Some(subscripts(p, lhs, false));
}
let lhs = member_or_new_expr(p, true)?;
if lhs.kind() == ARROW_EXPR {
return Some(lhs);
}
let m = lhs.precede(p);
let type_args = if p.at(T![<]) {
let checkpoint = p.checkpoint();
let mut complete = try_parse_ts(p, |p| ts_type_args(p));
if !p.at(T!['(']) {
p.rewind(checkpoint);
None
} else {
if let Some(ref mut comp) = complete {
comp.err_if_not_ts(p, "type arguments can only be used in TypeScript files");
}
complete
}
} else {
None
};
if p.at(T!['(']) {
args(p);
let lhs = m.complete(p, CALL_EXPR);
return Some(subscripts(p, lhs, false));
}
if type_args.is_some() {
p.expect(T!['(']);
}
m.abandon(p);
Some(lhs)
}
pub fn postfix_expr(p: &mut Parser) -> Option<CompletedMarker> {
let lhs = lhs_expr(p);
if !p.has_linebreak_before_n(0) {
match p.cur() {
T![++] => {
check_assign_target_from_marker(p, &lhs?);
let m = lhs?.precede(p);
p.bump(T![++]);
let complete = m.complete(p, UNARY_EXPR);
Some(complete)
}
T![--] => {
check_assign_target_from_marker(p, &lhs?);
let m = lhs?.precede(p);
p.bump(T![--]);
let complete = m.complete(p, UNARY_EXPR);
Some(complete)
}
_ => lhs,
}
} else {
lhs
}
}
pub fn unary_expr(p: &mut Parser) -> Option<CompletedMarker> {
const UNARY_SINGLE: TokenSet =
token_set![T![delete], T![void], T![typeof], T![+], T![-], T![~], T![!]];
if (p.state.in_async || p.syntax.top_level_await) && p.at(T![await]) {
let m = p.start();
p.bump_any();
unary_expr(p);
return Some(m.complete(p, AWAIT_EXPR));
}
if p.at(T![<]) {
let m = p.start();
p.bump_any();
if p.eat(T![const]) {
p.expect(T![>]);
unary_expr(p);
let mut res = m.complete(p, TS_CONST_ASSERTION);
res.err_if_not_ts(p, "const assertions can only be used in TypeScript files");
return Some(res);
} else {
ts_type(p);
p.expect(T![>]);
unary_expr(p);
let mut res = m.complete(p, TS_ASSERTION);
res.err_if_not_ts(p, "type assertions can only be used in TypeScript files");
return Some(res);
}
}
if p.at(T![++]) {
let m = p.start();
p.bump(T![++]);
let right = unary_expr(p)?;
let complete = m.complete(p, UNARY_EXPR);
check_assign_target_from_marker(p, &right);
return Some(complete);
}
if p.at(T![--]) {
let m = p.start();
p.bump(T![--]);
let right = unary_expr(p)?;
let complete = m.complete(p, UNARY_EXPR);
check_assign_target_from_marker(p, &right);
return Some(complete);
}
if p.at_ts(UNARY_SINGLE) {
let m = p.start();
let op = p.cur();
p.bump_any();
let res = unary_expr(p)?;
if op == T![delete] && p.typescript() {
match res.kind() {
DOT_EXPR | BRACKET_EXPR => {}
_ => {
let err = p
.err_builder("the target for a delete operator must be a property access")
.primary(res.range(p), "");
p.error(err);
}
}
}
return Some(m.complete(p, UNARY_EXPR));
}
postfix_expr(p)
}