oxc/crates/oxc_prettier/src/needs_parens.rs
Yuji Sugiura 521df425de
refactor(prettier): Unified method of Doc generation (#7600)
Before:

- `Doc::Xxx()` + `XXX.with_xxx()`
- `macro!()`
- `p.xxx()`

After:

- `p.xxx()` with `p_vec!` macro
2024-12-03 17:27:55 +08:00

679 lines
30 KiB
Rust

//! Direct port of needs-parens for adding or removing parentheses.
//!
//! See <https://github.com/prettier/prettier/blob/3.3.3/src/language-js/needs-parens.js>
use oxc_ast::{
ast::{
match_member_expression, AssignmentTarget, ChainElement, ExportDefaultDeclarationKind,
Expression, ForStatementInit, ForStatementLeft, MemberExpression, ModuleDeclaration,
ObjectExpression, SimpleAssignmentTarget,
},
AstKind,
};
use oxc_span::{GetSpan, Span};
use oxc_syntax::{
operator::{BinaryOperator, UnaryOperator, UpdateOperator},
precedence::GetPrecedence,
};
use crate::{binaryish::BinaryishOperator, Prettier};
impl<'a> Prettier<'a> {
// NOTE: Why this takes `mut`...?
pub(crate) fn need_parens(&mut self, kind: AstKind<'a>) -> bool {
if matches!(kind, AstKind::Program(_)) || kind.is_statement() || kind.is_declaration() {
return false;
}
if matches!(kind, AstKind::ObjectExpression(e) if self.check_object_expression(e))
|| self.check_for_of_stmt_head_starts_with_async_or_let(kind)
|| self.check_for_of_stmt_head_starts_with_let(kind)
|| self.check_let_object(kind)
|| self.check_parent_kind(kind)
|| self.check_kind(kind)
{
return true;
}
false
}
fn check_kind(&self, kind: AstKind<'a>) -> bool {
let parent_kind = self.parent_kind();
let parent_parent_kind = self.parent_parent_kind();
match kind {
AstKind::NumericLiteral(literal) => {
matches!(parent_kind, AstKind::MemberExpression(e) if e.object().span() == literal.span)
}
AstKind::SequenceExpression(e) => self.check_sequence_expression(e.span),
AstKind::ObjectExpression(e) => self.check_object_function_class(e.span),
AstKind::Function(f) if f.is_expression() => {
if self.check_object_function_class(f.span) {
return true;
}
match parent_kind {
AstKind::CallExpression(call_expr) => call_expr.callee.span() == f.span,
AstKind::NewExpression(new_expr) => new_expr.callee.span() == f.span,
AstKind::TaggedTemplateExpression(_) => true,
_ => false,
}
}
AstKind::Class(c) if c.is_expression() => {
if self.check_object_function_class(c.span) {
return true;
}
if let AstKind::NewExpression(new_expr) = parent_kind {
return new_expr.callee.span() == c.span;
}
false
}
AstKind::AssignmentExpression(assign_expr) => match parent_kind {
AstKind::ArrowFunctionExpression(arrow_expr)
if arrow_expr
.get_expression()
.is_some_and(|e| e.span() == assign_expr.span) =>
{
true
}
AstKind::AssignmentExpression(_) => false,
AstKind::ForStatement(stmt)
if stmt.init.as_ref().is_some_and(|e| e.span() == assign_expr.span)
|| stmt.update.as_ref().is_some_and(|e| e.span() == assign_expr.span) =>
{
false
}
AstKind::ExpressionStatement(_) => {
matches!(assign_expr.left, AssignmentTarget::ObjectAssignmentTarget(_))
}
AstKind::SequenceExpression(sequence_expr) => {
!matches!(parent_parent_kind, Some(AstKind::ForStatement(for_stat))
if for_stat
.init
.as_ref()
.is_some_and(|e| e.span() == sequence_expr.span)
|| for_stat
.update
.as_ref()
.is_some_and(|e| e.span() == sequence_expr.span))
}
_ => true,
},
AstKind::UpdateExpression(update_expr) => match parent_kind {
AstKind::UnaryExpression(unary_expr) => {
update_expr.prefix
&& ((update_expr.operator == UpdateOperator::Increment
&& unary_expr.operator == UnaryOperator::UnaryPlus)
|| (update_expr.operator == UpdateOperator::Decrement
&& unary_expr.operator == UnaryOperator::UnaryNegation))
}
_ => self.check_update_unary(update_expr.span),
},
AstKind::UnaryExpression(unary_expr) => match parent_kind {
AstKind::UnaryExpression(parent_expr) => {
let u_op = unary_expr.operator;
u_op == parent_expr.operator
&& (matches!(u_op, UnaryOperator::UnaryPlus | UnaryOperator::UnaryNegation))
}
_ => self.check_update_unary(unary_expr.span),
},
AstKind::YieldExpression(e) => match parent_kind {
AstKind::AwaitExpression(_) => true,
_ => self.check_yield_await(e.span),
},
AstKind::AwaitExpression(e) => self.check_yield_await(e.span),
AstKind::TSTypeAssertion(e) => self.check_binarish(e.span),
AstKind::TSAsExpression(e) => self.check_binarish(e.span),
AstKind::TSSatisfiesExpression(e) => self.check_binarish(e.span),
AstKind::LogicalExpression(e) => self.check_binarish(e.span),
AstKind::BinaryExpression(e) => match parent_kind {
AstKind::UpdateExpression(_) => true,
_ if e.operator.is_in() && self.is_path_in_for_statement_initializer(e.span) => {
true
}
_ => self.check_binarish(e.span),
},
AstKind::MemberExpression(e) => self.check_member_call(e.span()),
AstKind::CallExpression(e) => self.check_member_call(e.span),
AstKind::TaggedTemplateExpression(e) => {
self.check_member_call_tagged_template_ts_non_null(e.span)
}
AstKind::TSNonNullExpression(e) => {
self.check_member_call_tagged_template_ts_non_null(e.span)
}
AstKind::ConditionalExpression(e) => match parent_kind {
AstKind::TaggedTemplateExpression(_)
| AstKind::UnaryExpression(_)
| AstKind::SpreadElement(_)
| AstKind::BinaryExpression(_)
| AstKind::LogicalExpression(_)
| AstKind::ModuleDeclaration(ModuleDeclaration::ExportDefaultDeclaration(_))
| AstKind::AwaitExpression(_)
| AstKind::JSXSpreadAttribute(_)
| AstKind::TSAsExpression(_)
| AstKind::TSSatisfiesExpression(_)
| AstKind::TSNonNullExpression(_) => true,
AstKind::CallExpression(call_expr) => call_expr.callee.span() == e.span,
AstKind::NewExpression(new_expr) => new_expr.callee.span() == e.span,
AstKind::ConditionalExpression(cond_expr) => cond_expr.test.span() == e.span,
AstKind::MemberExpression(member_expr) => member_expr.object().span() == e.span,
_ => false,
},
AstKind::Function(e) if e.is_expression() => match parent_kind {
AstKind::CallExpression(call_expr) => call_expr.callee.span() == e.span,
AstKind::NewExpression(new_expr) => new_expr.callee.span() == e.span,
AstKind::TaggedTemplateExpression(_) => true,
_ => false,
},
AstKind::ArrowFunctionExpression(e) => match parent_kind {
AstKind::CallExpression(call_expr) => call_expr.callee.span() == e.span,
AstKind::NewExpression(new_expr) => new_expr.callee.span() == e.span,
AstKind::MemberExpression(member_expr) => member_expr.object().span() == e.span,
AstKind::TSAsExpression(_)
| AstKind::TSSatisfiesExpression(_)
| AstKind::TSNonNullExpression(_)
| AstKind::TaggedTemplateExpression(_)
| AstKind::UnaryExpression(_)
| AstKind::LogicalExpression(_)
| AstKind::AwaitExpression(_)
| AstKind::TSTypeAssertion(_) => true,
AstKind::ConditionalExpression(cond_expr) => cond_expr.test.span() == e.span,
_ => false,
},
AstKind::Class(class) if class.is_expression() => match parent_kind {
AstKind::NewExpression(new_expr) => new_expr.callee.span() == class.span,
_ => false,
},
_ => false,
}
}
fn check_parent_kind(&mut self, kind: AstKind<'a>) -> bool {
match self.parent_kind() {
AstKind::Class(class) => {
if let Some(h) = &class.super_class {
match kind {
AstKind::ArrowFunctionExpression(e) if e.span == h.span() => return true,
AstKind::AssignmentExpression(e) if e.span == h.span() => return true,
AstKind::AwaitExpression(e) if e.span == h.span() => return true,
AstKind::BinaryExpression(e) if e.span == h.span() => return true,
AstKind::ConditionalExpression(e) if e.span == h.span() => return true,
AstKind::LogicalExpression(e) if e.span == h.span() => return true,
AstKind::NewExpression(e) if e.span == h.span() => return true,
AstKind::ObjectExpression(e) if e.span == h.span() => return true,
AstKind::SequenceExpression(e) if e.span == h.span() => return true,
AstKind::TaggedTemplateExpression(e) if e.span == h.span() => return true,
AstKind::UnaryExpression(e) if e.span == h.span() => return true,
AstKind::UpdateExpression(e) if e.span == h.span() => return true,
AstKind::YieldExpression(e) if e.span == h.span() => return true,
AstKind::TSNonNullExpression(e) if e.span == h.span() => return true,
AstKind::Class(e)
if e.is_expression()
&& !e.decorators.is_empty()
&& e.span == h.span() =>
{
return true;
}
_ => {}
}
}
}
AstKind::ModuleDeclaration(ModuleDeclaration::ExportDefaultDeclaration(decl)) => {
return matches!(
decl.declaration,
ExportDefaultDeclarationKind::SequenceExpression(_)
) || (decl.declaration.is_expression()
&& self.should_wrap_function_for_export_default());
}
AstKind::BinaryExpression(binary_expr) => {
if binary_expr.operator.is_relational() {
if let AstKind::UnaryExpression(unary_expr) = kind {
if binary_expr.left.span() == unary_expr.span {
return true;
}
}
}
}
_ => {}
}
false
}
fn check_sequence_expression(&self, span: Span) -> bool {
match self.parent_kind() {
AstKind::ReturnStatement(_) | AstKind::ForStatement(_) => false,
AstKind::ExpressionStatement(expr) => expr.expression.span() != span,
AstKind::ArrowFunctionExpression(expr) => expr.body.span != span,
_ => true,
}
}
fn check_object_expression(&self, obj_expr: &ObjectExpression<'a>) -> bool {
let mut arrow_expr = None;
for kind in self.stack.iter().rev() {
if let AstKind::ArrowFunctionExpression(e) = kind {
e.get_expression();
arrow_expr = Some(e);
break;
}
}
if let Some(arrow_expr) = arrow_expr {
if let Some(e) = arrow_expr.get_expression() {
if !matches!(
e,
Expression::SequenceExpression(_) | Expression::AssignmentExpression(_)
) && Self::starts_with_no_lookahead_token(e, obj_expr.span)
{
return true;
}
}
}
false
}
/// `for ((async) of []);` and `for ((let) of []);`
fn check_for_of_stmt_head_starts_with_async_or_let(&self, kind: AstKind<'a>) -> bool {
let AstKind::IdentifierReference(ident) = kind else { return false };
let AstKind::ForOfStatement(stmt) = self.parent_kind() else { return false };
if let ForStatementLeft::AssignmentTargetIdentifier(i) = &stmt.left {
if (i.span == ident.span) && (i.name == "let" || (i.name == "async" && !stmt.r#await)) {
return true;
}
}
false
}
/// `for ((let.a) of []);`
fn check_for_of_stmt_head_starts_with_let(&self, kind: AstKind<'a>) -> bool {
let AstKind::IdentifierReference(ident) = kind else { return false };
if ident.name != "let" {
return false;
}
for kind in self.stack.iter().rev() {
if let AstKind::ForOfStatement(stmt) = kind {
if let Some(target) = stmt.left.as_assignment_target() {
if let Some(e) = target.as_member_expression() {
return Self::starts_with_no_lookahead_token(e.object(), ident.span);
}
}
break;
}
}
false
}
/// `(let)[a] = 1`
fn check_let_object(&self, kind: AstKind<'a>) -> bool {
let AstKind::IdentifierReference(ident) = kind else { return false };
if ident.name != "let" {
return false;
}
let AstKind::MemberExpression(MemberExpression::ComputedMemberExpression(expr)) =
self.parent_kind()
else {
return false;
};
if !matches!(&expr.object, Expression::Identifier(ident) if ident.name == "let") {
return false;
}
let Some(statement) = self.stack.iter().rev().find(|node| {
matches!(
node,
AstKind::ExpressionStatement(_)
| AstKind::ForStatement(_)
| AstKind::ForInStatement(_)
)
}) else {
return false;
};
match statement {
AstKind::ExpressionStatement(stmt) => {
Self::starts_with_no_lookahead_token(&stmt.expression, ident.span)
}
AstKind::ForStatement(stmt) => stmt
.init
.as_ref()
.and_then(ForStatementInit::as_expression)
.map_or(false, |e| Self::starts_with_no_lookahead_token(e, ident.span)),
AstKind::ForInStatement(stmt) => {
if let Some(target) = stmt.left.as_assignment_target() {
if let Some(e) = target.as_member_expression() {
return Self::starts_with_no_lookahead_token(e.object(), ident.span);
}
}
false
}
_ => false,
}
}
fn check_object_function_class(&self, span: Span) -> bool {
for ast_kind in self.stack.iter().rev() {
if let AstKind::ExpressionStatement(e) = ast_kind {
if Self::starts_with_no_lookahead_token(&e.expression, span) {
return true;
}
}
}
false
}
fn check_update_unary(&self, span: Span) -> bool {
match self.parent_kind() {
AstKind::MemberExpression(member_expr) => member_expr.object().span() == span,
AstKind::CallExpression(call_expr) => call_expr.callee.span() == span,
AstKind::NewExpression(new_expr) => new_expr.callee.span() == span,
AstKind::BinaryExpression(bin_expr) => {
bin_expr.left.span() == span && bin_expr.operator == BinaryOperator::Exponential
}
AstKind::TaggedTemplateExpression(_) | AstKind::TSNonNullExpression(_) => true,
_ => false,
}
}
fn check_yield_await(&self, span: Span) -> bool {
match self.parent_kind() {
AstKind::TaggedTemplateExpression(_)
| AstKind::UnaryExpression(_)
| AstKind::LogicalExpression(_)
| AstKind::SpreadElement(_)
| AstKind::TSAsExpression(_)
| AstKind::TSSatisfiesExpression(_)
| AstKind::TSNonNullExpression(_)
| AstKind::BinaryExpression(_) => true,
AstKind::MemberExpression(member_expr) => member_expr.object().span() == span,
AstKind::NewExpression(new_expr) => new_expr.callee.span() == span,
AstKind::CallExpression(new_expr) => new_expr.callee.span() == span,
AstKind::ConditionalExpression(con_expr) => con_expr.test.span() == span,
_ => false,
}
}
fn check_binarish(&self, span: Span) -> bool {
let current_kind = self.current_kind();
let parent_kind = self.parent_kind();
match parent_kind {
AstKind::TSAsExpression(_) | AstKind::TSSatisfiesExpression(_) => {
return !self.is_binary_cast_expression(span)
}
AstKind::ConditionalExpression(_) => return self.is_binary_cast_expression(span),
AstKind::NewExpression(new_expr) => return new_expr.callee.span() == span,
AstKind::CallExpression(new_expr) => return new_expr.callee.span() == span,
AstKind::Class(class) => {
return class.super_class.as_ref().is_some_and(|e| e.span() == span);
}
AstKind::TSTypeAssertion(_)
| AstKind::TaggedTemplateExpression(_)
| AstKind::UnaryExpression(_)
| AstKind::JSXSpreadAttribute(_)
| AstKind::SpreadElement(_)
| AstKind::AwaitExpression(_)
| AstKind::TSNonNullExpression(_)
| AstKind::UpdateExpression(_) => return true,
AstKind::MemberExpression(member_expr) => return member_expr.object().span() == span,
AstKind::AssignmentExpression(assign_expr) => {
return assign_expr.left.span() == span && self.is_binary_cast_expression(span);
}
AstKind::AssignmentPattern(assign_pat) => {
return assign_pat.left.span() == span && self.is_binary_cast_expression(span);
}
AstKind::LogicalExpression(parent_logical_expr) => {
if let AstKind::LogicalExpression(logical_expr) = current_kind {
return parent_logical_expr.operator != logical_expr.operator;
}
}
_ => {}
}
let operator = match self.current_kind() {
AstKind::LogicalExpression(e) => BinaryishOperator::from(e.operator),
AstKind::BinaryExpression(e) => BinaryishOperator::from(e.operator),
_ => return false,
};
let parent_operator = match parent_kind {
AstKind::LogicalExpression(e) => BinaryishOperator::from(e.operator),
AstKind::BinaryExpression(e) => BinaryishOperator::from(e.operator),
_ => return false,
};
let precedence = operator.precedence();
let parent_precedence = parent_operator.precedence();
if parent_precedence > precedence {
return true;
}
if parent_precedence == precedence && !parent_operator.should_flatten(operator) {
return true;
}
if parent_precedence < precedence
&& matches!(operator, BinaryishOperator::BinaryOperator(op) if op == BinaryOperator::Remainder)
{
return matches!(parent_operator, BinaryishOperator::BinaryOperator(op) if matches!(op, BinaryOperator::Addition | BinaryOperator::Subtraction));
}
// Add parenthesis when working with bitwise operators
// It's not strictly needed but helps with code understanding
if matches!(parent_kind, AstKind::BinaryExpression(binary_expr) if binary_expr.operator.is_bitwise())
{
return true;
}
false
}
fn check_member_call(&self, span: Span) -> bool {
// if (shouldAddParenthesesToChainElement(path)) {
// return true;
// }
self.check_member_call_tagged_template_ts_non_null(span)
}
fn check_member_call_tagged_template_ts_non_null(&self, span: Span) -> bool {
match self.parent_kind() {
AstKind::NewExpression(new_expr) if new_expr.callee.span() == span => {
let mut object = &new_expr.callee;
loop {
object = match object {
Expression::CallExpression(_) => return true,
Expression::ComputedMemberExpression(e) => &e.object,
Expression::StaticMemberExpression(e) => &e.object,
Expression::PrivateFieldExpression(e) => &e.object,
Expression::TaggedTemplateExpression(e) => &e.tag,
Expression::TSNonNullExpression(e) => &e.expression,
_ => return false,
}
}
}
_ => false,
}
}
fn should_wrap_function_for_export_default(&mut self) -> bool {
let kind = self.current_kind();
let b = matches!(
self.parent_kind(),
AstKind::ModuleDeclaration(ModuleDeclaration::ExportDefaultDeclaration(_))
);
if matches!(kind, AstKind::Function(f) if f.is_expression())
|| matches!(kind, AstKind::Class(c) if c.is_expression())
{
return b || !self.need_parens(self.current_kind());
}
if !Self::has_naked_left_side(kind) || (!b && self.need_parens(self.current_kind())) {
return false;
}
let lhs = Self::get_left_side_path_name(kind);
self.stack.push(lhs);
let result = self.should_wrap_function_for_export_default();
self.stack.pop();
result
}
fn has_naked_left_side(kind: AstKind<'a>) -> bool {
matches!(
kind,
AstKind::AssignmentExpression(_)
| AstKind::BinaryExpression(_)
| AstKind::LogicalExpression(_)
| AstKind::ConditionalExpression(_)
| AstKind::CallExpression(_)
| AstKind::MemberExpression(_)
| AstKind::SequenceExpression(_)
| AstKind::TaggedTemplateExpression(_)
| AstKind::TSNonNullExpression(_)
| AstKind::ChainExpression(_)
) || matches!(kind, AstKind::UpdateExpression(e) if !e.prefix)
}
fn get_left_side_path_name(kind: AstKind<'a>) -> AstKind<'a> {
match kind {
AstKind::CallExpression(e) => AstKind::from_expression(&e.callee),
AstKind::ConditionalExpression(e) => AstKind::from_expression(&e.test),
AstKind::TaggedTemplateExpression(e) => AstKind::from_expression(&e.tag),
AstKind::AssignmentExpression(e) => AstKind::AssignmentTarget(&e.left),
AstKind::MemberExpression(e) => AstKind::from_expression(e.object()),
AstKind::BinaryExpression(e) => AstKind::from_expression(&e.left),
AstKind::LogicalExpression(e) => AstKind::from_expression(&e.left),
_ => panic!("need to handle {}", kind.debug_name()),
}
}
fn is_binary_cast_expression(&self, _span: Span) -> bool {
false
}
fn is_path_in_for_statement_initializer(&self, span: Span) -> bool {
let mut node = Some(span);
let mut parents = self.stack.iter().rev();
while let Some(n) = node {
let parent = parents.next();
if let Some(AstKind::ForStatement(stmt)) = parent {
if stmt.init.as_ref().is_some_and(|init| init.span() == n) {
return true;
}
}
node = parent.map(GetSpan::span);
}
false
}
fn starts_with_no_lookahead_token(e: &Expression<'a>, span: Span) -> bool {
match e {
Expression::BinaryExpression(e) => Self::starts_with_no_lookahead_token(&e.left, span),
Expression::LogicalExpression(e) => Self::starts_with_no_lookahead_token(&e.left, span),
Expression::AssignmentExpression(e) => match &e.left {
AssignmentTarget::AssignmentTargetIdentifier(_)
| AssignmentTarget::ArrayAssignmentTarget(_)
| AssignmentTarget::ObjectAssignmentTarget(_) => false,
AssignmentTarget::ComputedMemberExpression(e) => {
Self::starts_with_no_lookahead_token(&e.object, span)
}
AssignmentTarget::StaticMemberExpression(e) => {
Self::starts_with_no_lookahead_token(&e.object, span)
}
AssignmentTarget::PrivateFieldExpression(e) => {
Self::starts_with_no_lookahead_token(&e.object, span)
}
AssignmentTarget::TSAsExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
AssignmentTarget::TSSatisfiesExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
AssignmentTarget::TSNonNullExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
AssignmentTarget::TSTypeAssertion(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
AssignmentTarget::TSInstantiationExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
},
match_member_expression!(Expression) => {
Self::starts_with_no_lookahead_token(e.to_member_expression().object(), span)
}
Expression::TaggedTemplateExpression(e) => {
if matches!(e.tag, Expression::FunctionExpression(_)) {
return false;
}
Self::starts_with_no_lookahead_token(&e.tag, span)
}
Expression::CallExpression(e) => {
if matches!(e.callee, Expression::FunctionExpression(_)) {
return false;
}
Self::starts_with_no_lookahead_token(&e.callee, span)
}
Expression::ConditionalExpression(e) => {
Self::starts_with_no_lookahead_token(&e.test, span)
}
Expression::UpdateExpression(e) => {
!e.prefix
&& match &e.argument {
SimpleAssignmentTarget::AssignmentTargetIdentifier(_) => false,
SimpleAssignmentTarget::ComputedMemberExpression(e) => {
Self::starts_with_no_lookahead_token(&e.object, span)
}
SimpleAssignmentTarget::StaticMemberExpression(e) => {
Self::starts_with_no_lookahead_token(&e.object, span)
}
SimpleAssignmentTarget::PrivateFieldExpression(e) => {
Self::starts_with_no_lookahead_token(&e.object, span)
}
SimpleAssignmentTarget::TSAsExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
SimpleAssignmentTarget::TSSatisfiesExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
SimpleAssignmentTarget::TSNonNullExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
SimpleAssignmentTarget::TSTypeAssertion(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
SimpleAssignmentTarget::TSInstantiationExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
}
}
Expression::SequenceExpression(e) => e
.expressions
.first()
.map_or(false, |e| Self::starts_with_no_lookahead_token(e, span)),
Expression::ChainExpression(e) => match &e.expression {
ChainElement::CallExpression(e) => {
Self::starts_with_no_lookahead_token(&e.callee, span)
}
ChainElement::TSNonNullExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
ChainElement::ComputedMemberExpression(e) => {
Self::starts_with_no_lookahead_token(&e.object, span)
}
ChainElement::StaticMemberExpression(e) => {
Self::starts_with_no_lookahead_token(&e.object, span)
}
ChainElement::PrivateFieldExpression(e) => {
Self::starts_with_no_lookahead_token(&e.object, span)
}
},
Expression::TSSatisfiesExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
Expression::TSAsExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
Expression::TSNonNullExpression(e) => {
Self::starts_with_no_lookahead_token(&e.expression, span)
}
_ => e.span() == span,
}
}
}