mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
feat(ast)!: introduce ArrowFunctionBody enum (#24987)
## Summary
- Introduces `ArrowFunctionBody` to represent concise expression and block bodies explicitly.
- Removes the `ArrowFunctionExpression::expression` boolean.
- Makes invalid arrow body combinations unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct ArrowFunctionExpression<'a> {
pub expression: bool,
pub body: Box<'a, FunctionBody<'a>>,
}
```
to:
```rust
pub enum ArrowFunctionBody<'a> {
FunctionBody(Box<'a, FunctionBody<'a>>) = 64,
INHERIT(Expression<'a>),
}
pub struct ArrowFunctionExpression<'a> {
pub body: ArrowFunctionBody<'a>,
}
```
Concise arrow bodies are now stored directly as expressions instead of synthetic `FunctionBody` and `ExpressionStatement` nodes. Block bodies continue to use `FunctionBody`.
## Why?
The previous representation required consumers to maintain this implicit invariant:
```text
expression == true
→ no directives
→ exactly one statement
→ that statement is an ExpressionStatement
```
Invalid combinations could cause panics, skipped work, or fallback behavior. The enum encodes the grammar directly and makes those invalid states unrepresentable.
Concise expressions also now have `ArrowFunctionExpression` as their direct traversal ancestor, without synthetic `FunctionBody` or `ExpressionStatement` nodes.
## How to migrate
Use the arrow body accessors or match on `ArrowFunctionBody` rather than inspecting `expression` and synthetic statements.
Common field migrations:
| Previous representation | New representation |
| --- | --- |
| `arrow.expression` | `arrow.is_expression()` |
| `arrow.get_expression()` | `arrow.get_expression()` |
| `&arrow.body.statements` | `arrow.get_function_body().map(\|body\| &body.statements)` |
| `&mut arrow.body.statements` | `arrow.get_function_body_mut().map(\|body\| &mut body.statements)` |
| Synthetic expression statement in `arrow.body` | Direct `ArrowFunctionBody` expression variant |
| `arrow.body = function_body` | `arrow.body = ArrowFunctionBody::FunctionBody(function_body)` |
| Expression-to-block conversion | Create a `FunctionBody` containing a return statement |
| Block-to-expression conversion | Assign the expression directly to `arrow.body` |
| Builder `(expression, body)` arguments | Pass a single `ArrowFunctionBody` |
closes https://github.com/oxc-project/backlog/issues/211
This commit is contained in:
1 parent
a922355564
commit
c917f204a7
166 files changed
+5291
-1496
No files matched your search
@@ -2542,16 +2542,112 @@ function deserializeFunctionBody(pos) {
|
||||
return node;
|
||||
}
|
||||
|
||||
function deserializeArrowFunctionBody(pos) {
|
||||
switch (uint8[pos]) {
|
||||
case 0:
|
||||
return deserializeBoxBooleanLiteral(pos + 8);
|
||||
case 1:
|
||||
return deserializeBoxNullLiteral(pos + 8);
|
||||
case 2:
|
||||
return deserializeBoxNumericLiteral(pos + 8);
|
||||
case 3:
|
||||
return deserializeBoxBigIntLiteral(pos + 8);
|
||||
case 4:
|
||||
return deserializeBoxRegExpLiteral(pos + 8);
|
||||
case 5:
|
||||
return deserializeBoxStringLiteral(pos + 8);
|
||||
case 6:
|
||||
return deserializeBoxTemplateLiteral(pos + 8);
|
||||
case 7:
|
||||
return deserializeBoxIdentifierReference(pos + 8);
|
||||
case 8:
|
||||
return deserializeBoxSuper(pos + 8);
|
||||
case 9:
|
||||
return deserializeBoxArrayExpression(pos + 8);
|
||||
case 10:
|
||||
return deserializeBoxArrowFunctionExpression(pos + 8);
|
||||
case 11:
|
||||
return deserializeBoxAssignmentExpression(pos + 8);
|
||||
case 12:
|
||||
return deserializeBoxAwaitExpression(pos + 8);
|
||||
case 13:
|
||||
return deserializeBoxBinaryExpression(pos + 8);
|
||||
case 14:
|
||||
return deserializeBoxCallExpression(pos + 8);
|
||||
case 15:
|
||||
return deserializeBoxChainExpression(pos + 8);
|
||||
case 16:
|
||||
return deserializeBoxClass(pos + 8);
|
||||
case 17:
|
||||
return deserializeBoxConditionalExpression(pos + 8);
|
||||
case 18:
|
||||
return deserializeBoxFunction(pos + 8);
|
||||
case 19:
|
||||
return deserializeBoxImportExpression(pos + 8);
|
||||
case 20:
|
||||
return deserializeBoxLogicalExpression(pos + 8);
|
||||
case 21:
|
||||
return deserializeBoxNewExpression(pos + 8);
|
||||
case 22:
|
||||
return deserializeBoxObjectExpression(pos + 8);
|
||||
case 23:
|
||||
return deserializeBoxParenthesizedExpression(pos + 8);
|
||||
case 24:
|
||||
return deserializeBoxSequenceExpression(pos + 8);
|
||||
case 25:
|
||||
return deserializeBoxTaggedTemplateExpression(pos + 8);
|
||||
case 26:
|
||||
return deserializeBoxThisExpression(pos + 8);
|
||||
case 27:
|
||||
return deserializeBoxUnaryExpression(pos + 8);
|
||||
case 28:
|
||||
return deserializeBoxUpdateExpression(pos + 8);
|
||||
case 29:
|
||||
return deserializeBoxYieldExpression(pos + 8);
|
||||
case 30:
|
||||
return deserializeBoxPrivateInExpression(pos + 8);
|
||||
case 31:
|
||||
return deserializeBoxImportMeta(pos + 8);
|
||||
case 32:
|
||||
return deserializeBoxNewTarget(pos + 8);
|
||||
case 33:
|
||||
return deserializeBoxJSXElement(pos + 8);
|
||||
case 34:
|
||||
return deserializeBoxJSXFragment(pos + 8);
|
||||
case 35:
|
||||
return deserializeBoxTSAsExpression(pos + 8);
|
||||
case 36:
|
||||
return deserializeBoxTSSatisfiesExpression(pos + 8);
|
||||
case 37:
|
||||
return deserializeBoxTSTypeAssertion(pos + 8);
|
||||
case 38:
|
||||
return deserializeBoxTSNonNullExpression(pos + 8);
|
||||
case 39:
|
||||
return deserializeBoxTSInstantiationExpression(pos + 8);
|
||||
case 40:
|
||||
return deserializeBoxV8IntrinsicExpression(pos + 8);
|
||||
case 48:
|
||||
return deserializeBoxComputedMemberExpression(pos + 8);
|
||||
case 49:
|
||||
return deserializeBoxStaticMemberExpression(pos + 8);
|
||||
case 50:
|
||||
return deserializeBoxPrivateFieldExpression(pos + 8);
|
||||
case 64:
|
||||
return deserializeBoxFunctionBody(pos + 8);
|
||||
default:
|
||||
throw Error(`Unexpected discriminant ${uint8[pos]} for ArrowFunctionBody`);
|
||||
}
|
||||
}
|
||||
|
||||
function deserializeArrowFunctionExpression(pos) {
|
||||
let expression = deserializeBool(pos + 48),
|
||||
start,
|
||||
let start,
|
||||
end,
|
||||
previousParent = parent,
|
||||
node = (parent = {
|
||||
__proto__: NodeProto,
|
||||
type: "ArrowFunctionExpression",
|
||||
expression,
|
||||
async: deserializeBool(pos + 49),
|
||||
expression: uint8[pos + 40] !== 64,
|
||||
async: deserializeBool(pos + 56),
|
||||
typeParameters: null,
|
||||
params: null,
|
||||
returnType: null,
|
||||
@@ -2562,16 +2658,11 @@ function deserializeArrowFunctionExpression(pos) {
|
||||
end: (end = deserializeI32(pos + 4)),
|
||||
range: [start, end],
|
||||
parent,
|
||||
}),
|
||||
body = deserializeBoxFunctionBody(pos + 40);
|
||||
if (expression === true) {
|
||||
body = body.body[0].expression;
|
||||
body.parent = parent;
|
||||
}
|
||||
});
|
||||
node.typeParameters = deserializeOptionBoxTSTypeParameterDeclaration(pos + 16);
|
||||
node.params = deserializeBoxFormalParameters(pos + 24);
|
||||
node.returnType = deserializeOptionBoxTSTypeAnnotation(pos + 32);
|
||||
node.body = body;
|
||||
node.body = deserializeArrowFunctionBody(pos + 40);
|
||||
parent = previousParent;
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1984,6 +1984,22 @@ pub struct FunctionBody<'a> {
|
||||
pub statements: Vec<'a, Statement<'a>>,
|
||||
}
|
||||
|
||||
/// The body of an [`ArrowFunctionExpression`].
|
||||
///
|
||||
/// Arrow functions have either a concise expression body (`() => expression`) or a block body
|
||||
/// (`() => { statements }`).
|
||||
#[ast(visit)]
|
||||
#[derive(Debug)]
|
||||
#[generate_derive(CloneIn, Dummy, ReplaceWith, TakeIn)]
|
||||
#[generate_derive(ContentEq, ESTree, GetAddress, GetSpan, GetSpanMut)]
|
||||
#[estree(no_ts_def)]
|
||||
pub enum ArrowFunctionBody<'a> {
|
||||
FunctionBody(Box<'a, FunctionBody<'a>>) = 64,
|
||||
|
||||
// `Expression` variants added here by `#[ast]` macro
|
||||
INHERIT(Expression<'a>),
|
||||
}
|
||||
|
||||
/// Arrow Function Definitions
|
||||
#[ast(visit)]
|
||||
#[scope(
|
||||
@@ -1993,22 +2009,22 @@ pub struct FunctionBody<'a> {
|
||||
#[derive(Debug)]
|
||||
#[generate_derive(CloneIn, Dummy, ReplaceWith, TakeIn)]
|
||||
#[generate_derive(ContentEq, ESTree, GetSpan, GetSpanMut, UnstableAddress)]
|
||||
#[estree(add_fields(id = Null, generator = False))]
|
||||
#[estree(
|
||||
add_fields(expression = ArrowFunctionExpressionExpression, id = Null, generator = False),
|
||||
field_order(expression, r#async, type_parameters, params, return_type, body, id, generator, span),
|
||||
)]
|
||||
pub struct ArrowFunctionExpression<'a> {
|
||||
pub node_id: Cell<NodeId>,
|
||||
pub span: Span,
|
||||
/// Is the function body an arrow expression? i.e. `() => expr` instead of `() => {}`
|
||||
pub expression: bool,
|
||||
pub r#async: bool,
|
||||
#[ts]
|
||||
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
|
||||
pub params: Box<'a, FormalParameters<'a>>,
|
||||
#[ts]
|
||||
pub return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
|
||||
/// See `expression` for whether this arrow expression returns an expression.
|
||||
// ESTree: https://github.com/estree/estree/blob/master/es2015.md#arrowfunctionexpression
|
||||
#[estree(via = ArrowFunctionExpressionBody)]
|
||||
pub body: Box<'a, FunctionBody<'a>>,
|
||||
#[estree(ts_type = "FunctionBody | Expression")]
|
||||
pub body: ArrowFunctionBody<'a>,
|
||||
pub scope_id: Cell<Option<ScopeId>>,
|
||||
/// `true` if the function is marked with a `/*#__NO_SIDE_EFFECTS__*/` comment
|
||||
#[builder(default)]
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::{
|
||||
fmt::{self, Display},
|
||||
};
|
||||
|
||||
use oxc_allocator::Box as ArenaBox;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_str::{Ident, Str};
|
||||
use oxc_syntax::{operator::UnaryOperator, scope::ScopeFlags, symbol::SymbolId};
|
||||
@@ -1579,25 +1580,71 @@ impl FunctionBody<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ArrowFunctionBody<'a> {
|
||||
/// Returns `true` if this arrow function has a block body.
|
||||
pub fn is_function_body(&self) -> bool {
|
||||
matches!(self, Self::FunctionBody(_))
|
||||
}
|
||||
|
||||
/// Returns the block body, if this arrow function has one.
|
||||
pub fn as_function_body(&self) -> Option<&FunctionBody<'a>> {
|
||||
match self {
|
||||
Self::FunctionBody(body) => Some(body),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the mutable block body, if this arrow function has one.
|
||||
pub fn as_function_body_mut(&mut self) -> Option<&mut FunctionBody<'a>> {
|
||||
match self {
|
||||
Self::FunctionBody(body) => Some(body),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this arrow function body into a block body, if it has one.
|
||||
pub fn into_function_body(self) -> Option<ArenaBox<'a, FunctionBody<'a>>> {
|
||||
match self {
|
||||
Self::FunctionBody(body) => Some(body),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this is an empty block body.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.as_function_body().is_some_and(FunctionBody::is_empty)
|
||||
}
|
||||
|
||||
/// Returns `true` if this block body has a `"use strict"` directive.
|
||||
pub fn has_use_strict_directive(&self) -> bool {
|
||||
self.as_function_body().is_some_and(FunctionBody::has_use_strict_directive)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ArrowFunctionExpression<'a> {
|
||||
/// Returns `true` if this arrow function has a concise expression body.
|
||||
pub fn is_expression(&self) -> bool {
|
||||
self.body.is_expression()
|
||||
}
|
||||
|
||||
/// Get expression part of `ArrowFunctionExpression`: `() => expression_part`.
|
||||
pub fn get_expression(&self) -> Option<&Expression<'a>> {
|
||||
if self.expression
|
||||
&& let Statement::ExpressionStatement(expr_stmt) = &self.body.statements[0]
|
||||
{
|
||||
return Some(&expr_stmt.expression);
|
||||
}
|
||||
None
|
||||
self.body.as_expression()
|
||||
}
|
||||
|
||||
/// Get expression part of `ArrowFunctionExpression`: `() => expression_part`.
|
||||
pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> {
|
||||
if self.expression
|
||||
&& let Statement::ExpressionStatement(expr_stmt) = &mut self.body.statements[0]
|
||||
{
|
||||
return Some(&mut expr_stmt.expression);
|
||||
}
|
||||
None
|
||||
self.body.as_expression_mut()
|
||||
}
|
||||
|
||||
/// Get block body of `ArrowFunctionExpression`: `() => { statements }`.
|
||||
pub fn get_function_body(&self) -> Option<&FunctionBody<'a>> {
|
||||
self.body.as_function_body()
|
||||
}
|
||||
|
||||
/// Get mutable block body of `ArrowFunctionExpression`: `() => { statements }`.
|
||||
pub fn get_function_body_mut(&mut self) -> Option<&mut FunctionBody<'a>> {
|
||||
self.body.as_function_body_mut()
|
||||
}
|
||||
|
||||
/// Returns `true` if this arrow function's body has a `"use strict"` directive.
|
||||
|
||||
@@ -702,8 +702,11 @@ const _: () = {
|
||||
assert!(offset_of!(FunctionBody, directives) == 16);
|
||||
assert!(offset_of!(FunctionBody, statements) == 40);
|
||||
|
||||
// Padding: 4 bytes
|
||||
assert!(size_of::<ArrowFunctionExpression>() == 56);
|
||||
assert!(size_of::<ArrowFunctionBody>() == 16);
|
||||
assert!(align_of::<ArrowFunctionBody>() == 8);
|
||||
|
||||
// Padding: 5 bytes
|
||||
assert!(size_of::<ArrowFunctionExpression>() == 64);
|
||||
assert!(align_of::<ArrowFunctionExpression>() == 8);
|
||||
assert!(offset_of!(ArrowFunctionExpression, span) == 0);
|
||||
assert!(offset_of!(ArrowFunctionExpression, node_id) == 8);
|
||||
@@ -712,10 +715,9 @@ const _: () = {
|
||||
assert!(offset_of!(ArrowFunctionExpression, params) == 24);
|
||||
assert!(offset_of!(ArrowFunctionExpression, return_type) == 32);
|
||||
assert!(offset_of!(ArrowFunctionExpression, body) == 40);
|
||||
assert!(offset_of!(ArrowFunctionExpression, expression) == 48);
|
||||
assert!(offset_of!(ArrowFunctionExpression, r#async) == 49);
|
||||
assert!(offset_of!(ArrowFunctionExpression, pure) == 50);
|
||||
assert!(offset_of!(ArrowFunctionExpression, pife) == 51);
|
||||
assert!(offset_of!(ArrowFunctionExpression, r#async) == 56);
|
||||
assert!(offset_of!(ArrowFunctionExpression, pure) == 57);
|
||||
assert!(offset_of!(ArrowFunctionExpression, pife) == 58);
|
||||
|
||||
// Padding: 3 bytes
|
||||
assert!(size_of::<YieldExpression>() == 32);
|
||||
@@ -2517,8 +2519,11 @@ const _: () = if cfg!(target_family = "wasm") || align_of::<u64>() == 8 {
|
||||
assert!(offset_of!(FunctionBody, directives) == 12);
|
||||
assert!(offset_of!(FunctionBody, statements) == 28);
|
||||
|
||||
// Padding: 0 bytes
|
||||
assert!(size_of::<ArrowFunctionExpression>() == 36);
|
||||
assert!(size_of::<ArrowFunctionBody>() == 8);
|
||||
assert!(align_of::<ArrowFunctionBody>() == 4);
|
||||
|
||||
// Padding: 1 bytes
|
||||
assert!(size_of::<ArrowFunctionExpression>() == 40);
|
||||
assert!(align_of::<ArrowFunctionExpression>() == 4);
|
||||
assert!(offset_of!(ArrowFunctionExpression, span) == 0);
|
||||
assert!(offset_of!(ArrowFunctionExpression, node_id) == 8);
|
||||
@@ -2527,10 +2532,9 @@ const _: () = if cfg!(target_family = "wasm") || align_of::<u64>() == 8 {
|
||||
assert!(offset_of!(ArrowFunctionExpression, params) == 20);
|
||||
assert!(offset_of!(ArrowFunctionExpression, return_type) == 24);
|
||||
assert!(offset_of!(ArrowFunctionExpression, body) == 28);
|
||||
assert!(offset_of!(ArrowFunctionExpression, expression) == 32);
|
||||
assert!(offset_of!(ArrowFunctionExpression, r#async) == 33);
|
||||
assert!(offset_of!(ArrowFunctionExpression, pure) == 34);
|
||||
assert!(offset_of!(ArrowFunctionExpression, pife) == 35);
|
||||
assert!(offset_of!(ArrowFunctionExpression, r#async) == 36);
|
||||
assert!(offset_of!(ArrowFunctionExpression, pure) == 37);
|
||||
assert!(offset_of!(ArrowFunctionExpression, pife) == 38);
|
||||
|
||||
// Padding: 3 bytes
|
||||
assert!(size_of::<YieldExpression>() == 24);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -2301,6 +2301,72 @@ impl<'new_alloc> CloneIn<'new_alloc> for FunctionBody<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'new_alloc> CloneIn<'new_alloc> for ArrowFunctionBody<'_> {
|
||||
type Cloned = ArrowFunctionBody<'new_alloc>;
|
||||
|
||||
fn clone_in_impl(
|
||||
&self,
|
||||
with_semantic_ids: CloneInSemanticIds,
|
||||
allocator: &'new_alloc Allocator,
|
||||
) -> Self::Cloned {
|
||||
match self {
|
||||
Self::FunctionBody(it) => ArrowFunctionBody::FunctionBody(CloneIn::clone_in_impl(
|
||||
it,
|
||||
with_semantic_ids,
|
||||
allocator,
|
||||
)),
|
||||
Self::BooleanLiteral(_)
|
||||
| Self::NullLiteral(_)
|
||||
| Self::NumericLiteral(_)
|
||||
| Self::BigIntLiteral(_)
|
||||
| Self::RegExpLiteral(_)
|
||||
| Self::StringLiteral(_)
|
||||
| Self::TemplateLiteral(_)
|
||||
| Self::Identifier(_)
|
||||
| Self::Super(_)
|
||||
| Self::ArrayExpression(_)
|
||||
| Self::ArrowFunctionExpression(_)
|
||||
| Self::AssignmentExpression(_)
|
||||
| Self::AwaitExpression(_)
|
||||
| Self::BinaryExpression(_)
|
||||
| Self::CallExpression(_)
|
||||
| Self::ChainExpression(_)
|
||||
| Self::ClassExpression(_)
|
||||
| Self::ConditionalExpression(_)
|
||||
| Self::FunctionExpression(_)
|
||||
| Self::ImportExpression(_)
|
||||
| Self::LogicalExpression(_)
|
||||
| Self::NewExpression(_)
|
||||
| Self::ObjectExpression(_)
|
||||
| Self::ParenthesizedExpression(_)
|
||||
| Self::SequenceExpression(_)
|
||||
| Self::TaggedTemplateExpression(_)
|
||||
| Self::ThisExpression(_)
|
||||
| Self::UnaryExpression(_)
|
||||
| Self::UpdateExpression(_)
|
||||
| Self::YieldExpression(_)
|
||||
| Self::PrivateInExpression(_)
|
||||
| Self::ImportMeta(_)
|
||||
| Self::NewTarget(_)
|
||||
| Self::JSXElement(_)
|
||||
| Self::JSXFragment(_)
|
||||
| Self::TSAsExpression(_)
|
||||
| Self::TSSatisfiesExpression(_)
|
||||
| Self::TSTypeAssertion(_)
|
||||
| Self::TSNonNullExpression(_)
|
||||
| Self::TSInstantiationExpression(_)
|
||||
| Self::V8IntrinsicExpression(_)
|
||||
| Self::ComputedMemberExpression(_)
|
||||
| Self::StaticMemberExpression(_)
|
||||
| Self::PrivateFieldExpression(_) => ArrowFunctionBody::from(CloneIn::clone_in_impl(
|
||||
self.to_expression(),
|
||||
with_semantic_ids,
|
||||
allocator,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'new_alloc> CloneIn<'new_alloc> for ArrowFunctionExpression<'_> {
|
||||
type Cloned = ArrowFunctionExpression<'new_alloc>;
|
||||
|
||||
@@ -2312,7 +2378,6 @@ impl<'new_alloc> CloneIn<'new_alloc> for ArrowFunctionExpression<'_> {
|
||||
ArrowFunctionExpression {
|
||||
node_id: CloneIn::clone_in_impl(&self.node_id, with_semantic_ids, allocator),
|
||||
span: CloneIn::clone_in_impl(&self.span, with_semantic_ids, allocator),
|
||||
expression: CloneIn::clone_in_impl(&self.expression, with_semantic_ids, allocator),
|
||||
r#async: CloneIn::clone_in_impl(&self.r#async, with_semantic_ids, allocator),
|
||||
type_parameters: CloneIn::clone_in_impl(
|
||||
&self.type_parameters,
|
||||
|
||||
@@ -1114,10 +1114,68 @@ impl ContentEq for FunctionBody<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentEq for ArrowFunctionBody<'_> {
|
||||
fn content_eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::FunctionBody(a), Self::FunctionBody(b)) => a.content_eq(b),
|
||||
(Self::BooleanLiteral(a), Self::BooleanLiteral(b)) => a.content_eq(b),
|
||||
(Self::NullLiteral(a), Self::NullLiteral(b)) => a.content_eq(b),
|
||||
(Self::NumericLiteral(a), Self::NumericLiteral(b)) => a.content_eq(b),
|
||||
(Self::BigIntLiteral(a), Self::BigIntLiteral(b)) => a.content_eq(b),
|
||||
(Self::RegExpLiteral(a), Self::RegExpLiteral(b)) => a.content_eq(b),
|
||||
(Self::StringLiteral(a), Self::StringLiteral(b)) => a.content_eq(b),
|
||||
(Self::TemplateLiteral(a), Self::TemplateLiteral(b)) => a.content_eq(b),
|
||||
(Self::Identifier(a), Self::Identifier(b)) => a.content_eq(b),
|
||||
(Self::Super(a), Self::Super(b)) => a.content_eq(b),
|
||||
(Self::ArrayExpression(a), Self::ArrayExpression(b)) => a.content_eq(b),
|
||||
(Self::ArrowFunctionExpression(a), Self::ArrowFunctionExpression(b)) => a.content_eq(b),
|
||||
(Self::AssignmentExpression(a), Self::AssignmentExpression(b)) => a.content_eq(b),
|
||||
(Self::AwaitExpression(a), Self::AwaitExpression(b)) => a.content_eq(b),
|
||||
(Self::BinaryExpression(a), Self::BinaryExpression(b)) => a.content_eq(b),
|
||||
(Self::CallExpression(a), Self::CallExpression(b)) => a.content_eq(b),
|
||||
(Self::ChainExpression(a), Self::ChainExpression(b)) => a.content_eq(b),
|
||||
(Self::ClassExpression(a), Self::ClassExpression(b)) => a.content_eq(b),
|
||||
(Self::ConditionalExpression(a), Self::ConditionalExpression(b)) => a.content_eq(b),
|
||||
(Self::FunctionExpression(a), Self::FunctionExpression(b)) => a.content_eq(b),
|
||||
(Self::ImportExpression(a), Self::ImportExpression(b)) => a.content_eq(b),
|
||||
(Self::LogicalExpression(a), Self::LogicalExpression(b)) => a.content_eq(b),
|
||||
(Self::NewExpression(a), Self::NewExpression(b)) => a.content_eq(b),
|
||||
(Self::ObjectExpression(a), Self::ObjectExpression(b)) => a.content_eq(b),
|
||||
(Self::ParenthesizedExpression(a), Self::ParenthesizedExpression(b)) => a.content_eq(b),
|
||||
(Self::SequenceExpression(a), Self::SequenceExpression(b)) => a.content_eq(b),
|
||||
(Self::TaggedTemplateExpression(a), Self::TaggedTemplateExpression(b)) => {
|
||||
a.content_eq(b)
|
||||
}
|
||||
(Self::ThisExpression(a), Self::ThisExpression(b)) => a.content_eq(b),
|
||||
(Self::UnaryExpression(a), Self::UnaryExpression(b)) => a.content_eq(b),
|
||||
(Self::UpdateExpression(a), Self::UpdateExpression(b)) => a.content_eq(b),
|
||||
(Self::YieldExpression(a), Self::YieldExpression(b)) => a.content_eq(b),
|
||||
(Self::PrivateInExpression(a), Self::PrivateInExpression(b)) => a.content_eq(b),
|
||||
(Self::ImportMeta(a), Self::ImportMeta(b)) => a.content_eq(b),
|
||||
(Self::NewTarget(a), Self::NewTarget(b)) => a.content_eq(b),
|
||||
(Self::JSXElement(a), Self::JSXElement(b)) => a.content_eq(b),
|
||||
(Self::JSXFragment(a), Self::JSXFragment(b)) => a.content_eq(b),
|
||||
(Self::TSAsExpression(a), Self::TSAsExpression(b)) => a.content_eq(b),
|
||||
(Self::TSSatisfiesExpression(a), Self::TSSatisfiesExpression(b)) => a.content_eq(b),
|
||||
(Self::TSTypeAssertion(a), Self::TSTypeAssertion(b)) => a.content_eq(b),
|
||||
(Self::TSNonNullExpression(a), Self::TSNonNullExpression(b)) => a.content_eq(b),
|
||||
(Self::TSInstantiationExpression(a), Self::TSInstantiationExpression(b)) => {
|
||||
a.content_eq(b)
|
||||
}
|
||||
(Self::V8IntrinsicExpression(a), Self::V8IntrinsicExpression(b)) => a.content_eq(b),
|
||||
(Self::ComputedMemberExpression(a), Self::ComputedMemberExpression(b)) => {
|
||||
a.content_eq(b)
|
||||
}
|
||||
(Self::StaticMemberExpression(a), Self::StaticMemberExpression(b)) => a.content_eq(b),
|
||||
(Self::PrivateFieldExpression(a), Self::PrivateFieldExpression(b)) => a.content_eq(b),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentEq for ArrowFunctionExpression<'_> {
|
||||
fn content_eq(&self, other: &Self) -> bool {
|
||||
ContentEq::content_eq(&self.expression, &other.expression)
|
||||
&& ContentEq::content_eq(&self.r#async, &other.r#async)
|
||||
ContentEq::content_eq(&self.r#async, &other.r#async)
|
||||
&& ContentEq::content_eq(&self.type_parameters, &other.type_parameters)
|
||||
&& ContentEq::content_eq(&self.params, &other.params)
|
||||
&& ContentEq::content_eq(&self.return_type, &other.return_type)
|
||||
|
||||
@@ -1261,15 +1261,23 @@ impl<'a> Dummy<'a> for FunctionBody<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Dummy<'a> for ArrowFunctionBody<'a> {
|
||||
/// Create a dummy [`ArrowFunctionBody`].
|
||||
///
|
||||
/// Has cost of making 1 allocation (16 bytes).
|
||||
fn dummy(allocator: &'a Allocator) -> Self {
|
||||
Self::NullLiteral(Dummy::dummy(allocator))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Dummy<'a> for ArrowFunctionExpression<'a> {
|
||||
/// Create a dummy [`ArrowFunctionExpression`].
|
||||
///
|
||||
/// Has cost of making 2 allocations (112 bytes).
|
||||
/// Has cost of making 2 allocations (64 bytes).
|
||||
fn dummy(allocator: &'a Allocator) -> Self {
|
||||
Self {
|
||||
node_id: Dummy::dummy(allocator),
|
||||
span: Dummy::dummy(allocator),
|
||||
expression: Dummy::dummy(allocator),
|
||||
r#async: Dummy::dummy(allocator),
|
||||
type_parameters: Dummy::dummy(allocator),
|
||||
params: Dummy::dummy(allocator),
|
||||
|
||||
@@ -1370,16 +1370,71 @@ impl ESTree for FunctionBody<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for ArrowFunctionBody<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::FunctionBody(it) => it.serialize(serializer),
|
||||
Self::BooleanLiteral(_)
|
||||
| Self::NullLiteral(_)
|
||||
| Self::NumericLiteral(_)
|
||||
| Self::BigIntLiteral(_)
|
||||
| Self::RegExpLiteral(_)
|
||||
| Self::StringLiteral(_)
|
||||
| Self::TemplateLiteral(_)
|
||||
| Self::Identifier(_)
|
||||
| Self::Super(_)
|
||||
| Self::ArrayExpression(_)
|
||||
| Self::ArrowFunctionExpression(_)
|
||||
| Self::AssignmentExpression(_)
|
||||
| Self::AwaitExpression(_)
|
||||
| Self::BinaryExpression(_)
|
||||
| Self::CallExpression(_)
|
||||
| Self::ChainExpression(_)
|
||||
| Self::ClassExpression(_)
|
||||
| Self::ConditionalExpression(_)
|
||||
| Self::FunctionExpression(_)
|
||||
| Self::ImportExpression(_)
|
||||
| Self::LogicalExpression(_)
|
||||
| Self::NewExpression(_)
|
||||
| Self::ObjectExpression(_)
|
||||
| Self::ParenthesizedExpression(_)
|
||||
| Self::SequenceExpression(_)
|
||||
| Self::TaggedTemplateExpression(_)
|
||||
| Self::ThisExpression(_)
|
||||
| Self::UnaryExpression(_)
|
||||
| Self::UpdateExpression(_)
|
||||
| Self::YieldExpression(_)
|
||||
| Self::PrivateInExpression(_)
|
||||
| Self::ImportMeta(_)
|
||||
| Self::NewTarget(_)
|
||||
| Self::JSXElement(_)
|
||||
| Self::JSXFragment(_)
|
||||
| Self::TSAsExpression(_)
|
||||
| Self::TSSatisfiesExpression(_)
|
||||
| Self::TSTypeAssertion(_)
|
||||
| Self::TSNonNullExpression(_)
|
||||
| Self::TSInstantiationExpression(_)
|
||||
| Self::V8IntrinsicExpression(_)
|
||||
| Self::ComputedMemberExpression(_)
|
||||
| Self::StaticMemberExpression(_)
|
||||
| Self::PrivateFieldExpression(_) => self.to_expression().serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for ArrowFunctionExpression<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", &JsonSafeString("ArrowFunctionExpression"));
|
||||
state.serialize_field("expression", &self.expression);
|
||||
state.serialize_field(
|
||||
"expression",
|
||||
&crate::serialize::js::ArrowFunctionExpressionExpression(self),
|
||||
);
|
||||
state.serialize_field("async", &self.r#async);
|
||||
state.serialize_ts_field("typeParameters", &self.type_parameters);
|
||||
state.serialize_field("params", &self.params);
|
||||
state.serialize_ts_field("returnType", &self.return_type);
|
||||
state.serialize_field("body", &crate::serialize::js::ArrowFunctionExpressionBody(self));
|
||||
state.serialize_field("body", &self.body);
|
||||
state.serialize_field("id", &crate::serialize::basic::Null(self));
|
||||
state.serialize_field("generator", &crate::serialize::basic::False(self));
|
||||
state.serialize_span(self.span);
|
||||
|
||||
@@ -433,6 +433,60 @@ impl GetAddress for BindingPattern<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl GetAddress for ArrowFunctionBody<'_> {
|
||||
// `#[inline]` because compiler should boil this down to a single assembly instruction
|
||||
#[inline]
|
||||
fn address(&self) -> Address {
|
||||
match self {
|
||||
Self::FunctionBody(it) => GetAddress::address(it),
|
||||
Self::BooleanLiteral(it) => GetAddress::address(it),
|
||||
Self::NullLiteral(it) => GetAddress::address(it),
|
||||
Self::NumericLiteral(it) => GetAddress::address(it),
|
||||
Self::BigIntLiteral(it) => GetAddress::address(it),
|
||||
Self::RegExpLiteral(it) => GetAddress::address(it),
|
||||
Self::StringLiteral(it) => GetAddress::address(it),
|
||||
Self::TemplateLiteral(it) => GetAddress::address(it),
|
||||
Self::Identifier(it) => GetAddress::address(it),
|
||||
Self::Super(it) => GetAddress::address(it),
|
||||
Self::ArrayExpression(it) => GetAddress::address(it),
|
||||
Self::ArrowFunctionExpression(it) => GetAddress::address(it),
|
||||
Self::AssignmentExpression(it) => GetAddress::address(it),
|
||||
Self::AwaitExpression(it) => GetAddress::address(it),
|
||||
Self::BinaryExpression(it) => GetAddress::address(it),
|
||||
Self::CallExpression(it) => GetAddress::address(it),
|
||||
Self::ChainExpression(it) => GetAddress::address(it),
|
||||
Self::ClassExpression(it) => GetAddress::address(it),
|
||||
Self::ConditionalExpression(it) => GetAddress::address(it),
|
||||
Self::FunctionExpression(it) => GetAddress::address(it),
|
||||
Self::ImportExpression(it) => GetAddress::address(it),
|
||||
Self::LogicalExpression(it) => GetAddress::address(it),
|
||||
Self::NewExpression(it) => GetAddress::address(it),
|
||||
Self::ObjectExpression(it) => GetAddress::address(it),
|
||||
Self::ParenthesizedExpression(it) => GetAddress::address(it),
|
||||
Self::SequenceExpression(it) => GetAddress::address(it),
|
||||
Self::TaggedTemplateExpression(it) => GetAddress::address(it),
|
||||
Self::ThisExpression(it) => GetAddress::address(it),
|
||||
Self::UnaryExpression(it) => GetAddress::address(it),
|
||||
Self::UpdateExpression(it) => GetAddress::address(it),
|
||||
Self::YieldExpression(it) => GetAddress::address(it),
|
||||
Self::PrivateInExpression(it) => GetAddress::address(it),
|
||||
Self::ImportMeta(it) => GetAddress::address(it),
|
||||
Self::NewTarget(it) => GetAddress::address(it),
|
||||
Self::JSXElement(it) => GetAddress::address(it),
|
||||
Self::JSXFragment(it) => GetAddress::address(it),
|
||||
Self::TSAsExpression(it) => GetAddress::address(it),
|
||||
Self::TSSatisfiesExpression(it) => GetAddress::address(it),
|
||||
Self::TSTypeAssertion(it) => GetAddress::address(it),
|
||||
Self::TSNonNullExpression(it) => GetAddress::address(it),
|
||||
Self::TSInstantiationExpression(it) => GetAddress::address(it),
|
||||
Self::V8IntrinsicExpression(it) => GetAddress::address(it),
|
||||
Self::ComputedMemberExpression(it) => GetAddress::address(it),
|
||||
Self::StaticMemberExpression(it) => GetAddress::address(it),
|
||||
Self::PrivateFieldExpression(it) => GetAddress::address(it),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetAddress for ClassElement<'_> {
|
||||
// `#[inline]` because compiler should boil this down to a single assembly instruction
|
||||
#[inline]
|
||||
|
||||
@@ -973,6 +973,58 @@ impl GetSpan for FunctionBody<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl GetSpan for ArrowFunctionBody<'_> {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::FunctionBody(it) => GetSpan::span(&**it),
|
||||
Self::BooleanLiteral(it) => GetSpan::span(&**it),
|
||||
Self::NullLiteral(it) => GetSpan::span(&**it),
|
||||
Self::NumericLiteral(it) => GetSpan::span(&**it),
|
||||
Self::BigIntLiteral(it) => GetSpan::span(&**it),
|
||||
Self::RegExpLiteral(it) => GetSpan::span(&**it),
|
||||
Self::StringLiteral(it) => GetSpan::span(&**it),
|
||||
Self::TemplateLiteral(it) => GetSpan::span(&**it),
|
||||
Self::Identifier(it) => GetSpan::span(&**it),
|
||||
Self::Super(it) => GetSpan::span(&**it),
|
||||
Self::ArrayExpression(it) => GetSpan::span(&**it),
|
||||
Self::ArrowFunctionExpression(it) => GetSpan::span(&**it),
|
||||
Self::AssignmentExpression(it) => GetSpan::span(&**it),
|
||||
Self::AwaitExpression(it) => GetSpan::span(&**it),
|
||||
Self::BinaryExpression(it) => GetSpan::span(&**it),
|
||||
Self::CallExpression(it) => GetSpan::span(&**it),
|
||||
Self::ChainExpression(it) => GetSpan::span(&**it),
|
||||
Self::ClassExpression(it) => GetSpan::span(&**it),
|
||||
Self::ConditionalExpression(it) => GetSpan::span(&**it),
|
||||
Self::FunctionExpression(it) => GetSpan::span(&**it),
|
||||
Self::ImportExpression(it) => GetSpan::span(&**it),
|
||||
Self::LogicalExpression(it) => GetSpan::span(&**it),
|
||||
Self::NewExpression(it) => GetSpan::span(&**it),
|
||||
Self::ObjectExpression(it) => GetSpan::span(&**it),
|
||||
Self::ParenthesizedExpression(it) => GetSpan::span(&**it),
|
||||
Self::SequenceExpression(it) => GetSpan::span(&**it),
|
||||
Self::TaggedTemplateExpression(it) => GetSpan::span(&**it),
|
||||
Self::ThisExpression(it) => GetSpan::span(&**it),
|
||||
Self::UnaryExpression(it) => GetSpan::span(&**it),
|
||||
Self::UpdateExpression(it) => GetSpan::span(&**it),
|
||||
Self::YieldExpression(it) => GetSpan::span(&**it),
|
||||
Self::PrivateInExpression(it) => GetSpan::span(&**it),
|
||||
Self::ImportMeta(it) => GetSpan::span(&**it),
|
||||
Self::NewTarget(it) => GetSpan::span(&**it),
|
||||
Self::JSXElement(it) => GetSpan::span(&**it),
|
||||
Self::JSXFragment(it) => GetSpan::span(&**it),
|
||||
Self::TSAsExpression(it) => GetSpan::span(&**it),
|
||||
Self::TSSatisfiesExpression(it) => GetSpan::span(&**it),
|
||||
Self::TSTypeAssertion(it) => GetSpan::span(&**it),
|
||||
Self::TSNonNullExpression(it) => GetSpan::span(&**it),
|
||||
Self::TSInstantiationExpression(it) => GetSpan::span(&**it),
|
||||
Self::V8IntrinsicExpression(it) => GetSpan::span(&**it),
|
||||
Self::ComputedMemberExpression(it) => GetSpan::span(&**it),
|
||||
Self::StaticMemberExpression(it) => GetSpan::span(&**it),
|
||||
Self::PrivateFieldExpression(it) => GetSpan::span(&**it),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetSpan for ArrowFunctionExpression<'_> {
|
||||
#[inline]
|
||||
fn span(&self) -> Span {
|
||||
|
||||
@@ -973,6 +973,58 @@ impl GetSpanMut for FunctionBody<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl GetSpanMut for ArrowFunctionBody<'_> {
|
||||
fn span_mut(&mut self) -> &mut Span {
|
||||
match self {
|
||||
Self::FunctionBody(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::BooleanLiteral(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::NullLiteral(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::NumericLiteral(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::BigIntLiteral(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::RegExpLiteral(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::StringLiteral(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::TemplateLiteral(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::Identifier(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::Super(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ArrayExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ArrowFunctionExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::AssignmentExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::AwaitExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::BinaryExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::CallExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ChainExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ClassExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ConditionalExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::FunctionExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ImportExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::LogicalExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::NewExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ObjectExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ParenthesizedExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::SequenceExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::TaggedTemplateExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ThisExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::UnaryExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::UpdateExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::YieldExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::PrivateInExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ImportMeta(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::NewTarget(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::JSXElement(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::JSXFragment(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::TSAsExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::TSSatisfiesExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::TSTypeAssertion(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::TSNonNullExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::TSInstantiationExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::V8IntrinsicExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::ComputedMemberExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::StaticMemberExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
Self::PrivateFieldExpression(it) => GetSpanMut::span_mut(&mut **it),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetSpanMut for ArrowFunctionExpression<'_> {
|
||||
#[inline]
|
||||
fn span_mut(&mut self) -> &mut Span {
|
||||
|
||||
@@ -192,6 +192,8 @@ impl<'a> ReplaceWith<'a> for FormalParameterRest<'a> {}
|
||||
|
||||
impl<'a> ReplaceWith<'a> for FunctionBody<'a> {}
|
||||
|
||||
impl<'a> ReplaceWith<'a> for ArrowFunctionBody<'a> {}
|
||||
|
||||
impl<'a> ReplaceWith<'a> for ArrowFunctionExpression<'a> {}
|
||||
|
||||
impl<'a> ReplaceWith<'a> for YieldExpression<'a> {}
|
||||
|
||||
@@ -194,6 +194,8 @@ impl<'a> TakeIn<'a> for FormalParameterRest<'a> {}
|
||||
|
||||
impl<'a> TakeIn<'a> for FunctionBody<'a> {}
|
||||
|
||||
impl<'a> TakeIn<'a> for ArrowFunctionBody<'a> {}
|
||||
|
||||
impl<'a> TakeIn<'a> for ArrowFunctionExpression<'a> {}
|
||||
|
||||
impl<'a> TakeIn<'a> for YieldExpression<'a> {}
|
||||
|
||||
@@ -3969,6 +3969,61 @@ impl BindingPattern<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrowFunctionBody<'_> {
|
||||
/// Get [`NodeId`] of [`ArrowFunctionBody`].
|
||||
// `#[inline(always)]` because this should boil down to a single instruction.
|
||||
#[inline(always)]
|
||||
pub fn node_id(&self) -> NodeId {
|
||||
match self {
|
||||
Self::FunctionBody(it) => it.node_id(),
|
||||
Self::BooleanLiteral(it) => it.node_id(),
|
||||
Self::NullLiteral(it) => it.node_id(),
|
||||
Self::NumericLiteral(it) => it.node_id(),
|
||||
Self::BigIntLiteral(it) => it.node_id(),
|
||||
Self::RegExpLiteral(it) => it.node_id(),
|
||||
Self::StringLiteral(it) => it.node_id(),
|
||||
Self::TemplateLiteral(it) => it.node_id(),
|
||||
Self::Identifier(it) => it.node_id(),
|
||||
Self::Super(it) => it.node_id(),
|
||||
Self::ArrayExpression(it) => it.node_id(),
|
||||
Self::ArrowFunctionExpression(it) => it.node_id(),
|
||||
Self::AssignmentExpression(it) => it.node_id(),
|
||||
Self::AwaitExpression(it) => it.node_id(),
|
||||
Self::BinaryExpression(it) => it.node_id(),
|
||||
Self::CallExpression(it) => it.node_id(),
|
||||
Self::ChainExpression(it) => it.node_id(),
|
||||
Self::ClassExpression(it) => it.node_id(),
|
||||
Self::ConditionalExpression(it) => it.node_id(),
|
||||
Self::FunctionExpression(it) => it.node_id(),
|
||||
Self::ImportExpression(it) => it.node_id(),
|
||||
Self::LogicalExpression(it) => it.node_id(),
|
||||
Self::NewExpression(it) => it.node_id(),
|
||||
Self::ObjectExpression(it) => it.node_id(),
|
||||
Self::ParenthesizedExpression(it) => it.node_id(),
|
||||
Self::SequenceExpression(it) => it.node_id(),
|
||||
Self::TaggedTemplateExpression(it) => it.node_id(),
|
||||
Self::ThisExpression(it) => it.node_id(),
|
||||
Self::UnaryExpression(it) => it.node_id(),
|
||||
Self::UpdateExpression(it) => it.node_id(),
|
||||
Self::YieldExpression(it) => it.node_id(),
|
||||
Self::PrivateInExpression(it) => it.node_id(),
|
||||
Self::ImportMeta(it) => it.node_id(),
|
||||
Self::NewTarget(it) => it.node_id(),
|
||||
Self::JSXElement(it) => it.node_id(),
|
||||
Self::JSXFragment(it) => it.node_id(),
|
||||
Self::TSAsExpression(it) => it.node_id(),
|
||||
Self::TSSatisfiesExpression(it) => it.node_id(),
|
||||
Self::TSTypeAssertion(it) => it.node_id(),
|
||||
Self::TSNonNullExpression(it) => it.node_id(),
|
||||
Self::TSInstantiationExpression(it) => it.node_id(),
|
||||
Self::V8IntrinsicExpression(it) => it.node_id(),
|
||||
Self::ComputedMemberExpression(it) => it.node_id(),
|
||||
Self::StaticMemberExpression(it) => it.node_id(),
|
||||
Self::PrivateFieldExpression(it) => it.node_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClassElement<'_> {
|
||||
/// Get [`NodeId`] of [`ClassElement`].
|
||||
// `#[inline(always)]` because this should boil down to a single instruction.
|
||||
|
||||
@@ -3895,6 +3895,407 @@ impl<'a> From<AssignmentTargetPattern<'a>> for ForStatementLeft<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ArrowFunctionBody<'a> {
|
||||
/// Return if an [`ArrowFunctionBody`] is an [`Expression`].
|
||||
#[inline]
|
||||
pub fn is_expression(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::BooleanLiteral(_)
|
||||
| Self::NullLiteral(_)
|
||||
| Self::NumericLiteral(_)
|
||||
| Self::BigIntLiteral(_)
|
||||
| Self::RegExpLiteral(_)
|
||||
| Self::StringLiteral(_)
|
||||
| Self::TemplateLiteral(_)
|
||||
| Self::Identifier(_)
|
||||
| Self::Super(_)
|
||||
| Self::ArrayExpression(_)
|
||||
| Self::ArrowFunctionExpression(_)
|
||||
| Self::AssignmentExpression(_)
|
||||
| Self::AwaitExpression(_)
|
||||
| Self::BinaryExpression(_)
|
||||
| Self::CallExpression(_)
|
||||
| Self::ChainExpression(_)
|
||||
| Self::ClassExpression(_)
|
||||
| Self::ConditionalExpression(_)
|
||||
| Self::FunctionExpression(_)
|
||||
| Self::ImportExpression(_)
|
||||
| Self::LogicalExpression(_)
|
||||
| Self::NewExpression(_)
|
||||
| Self::ObjectExpression(_)
|
||||
| Self::ParenthesizedExpression(_)
|
||||
| Self::SequenceExpression(_)
|
||||
| Self::TaggedTemplateExpression(_)
|
||||
| Self::ThisExpression(_)
|
||||
| Self::UnaryExpression(_)
|
||||
| Self::UpdateExpression(_)
|
||||
| Self::YieldExpression(_)
|
||||
| Self::PrivateInExpression(_)
|
||||
| Self::ImportMeta(_)
|
||||
| Self::NewTarget(_)
|
||||
| Self::JSXElement(_)
|
||||
| Self::JSXFragment(_)
|
||||
| Self::TSAsExpression(_)
|
||||
| Self::TSSatisfiesExpression(_)
|
||||
| Self::TSTypeAssertion(_)
|
||||
| Self::TSNonNullExpression(_)
|
||||
| Self::TSInstantiationExpression(_)
|
||||
| Self::V8IntrinsicExpression(_)
|
||||
| Self::ComputedMemberExpression(_)
|
||||
| Self::StaticMemberExpression(_)
|
||||
| Self::PrivateFieldExpression(_)
|
||||
)
|
||||
}
|
||||
|
||||
/// Convert an [`ArrowFunctionBody`] to an [`Expression`].
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if not convertible.
|
||||
#[inline]
|
||||
pub fn into_expression(self) -> Expression<'a> {
|
||||
Expression::try_from(self).unwrap()
|
||||
}
|
||||
|
||||
/// Convert an [`&ArrowFunctionBody`] to an [`&Expression`].
|
||||
///
|
||||
/// [`&ArrowFunctionBody`]: ArrowFunctionBody
|
||||
/// [`&Expression`]: Expression
|
||||
#[inline]
|
||||
pub fn as_expression(&self) -> Option<&Expression<'a>> {
|
||||
if self.is_expression() {
|
||||
// SAFETY: Transmute is safe because discriminants + types are identical between
|
||||
// `parent` and `child` for the shared variants
|
||||
Some(unsafe { NonNull::from_ref(self).cast::<Expression>().as_ref() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an [`&mut ArrowFunctionBody`] to an [`&mut Expression`].
|
||||
///
|
||||
/// [`&mut ArrowFunctionBody`]: ArrowFunctionBody
|
||||
/// [`&mut Expression`]: Expression
|
||||
#[inline]
|
||||
pub fn as_expression_mut(&mut self) -> Option<&mut Expression<'a>> {
|
||||
if self.is_expression() {
|
||||
// SAFETY: Transmute is safe because discriminants + types are identical between
|
||||
// `parent` and `child` for the shared variants
|
||||
Some(unsafe { NonNull::from_mut(self).cast::<Expression>().as_mut() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an [`&ArrowFunctionBody`] to an [`&Expression`].
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if not convertible.
|
||||
///
|
||||
/// [`&ArrowFunctionBody`]: ArrowFunctionBody
|
||||
/// [`&Expression`]: Expression
|
||||
#[inline]
|
||||
pub fn to_expression(&self) -> &Expression<'a> {
|
||||
self.as_expression().unwrap()
|
||||
}
|
||||
|
||||
/// Convert an [`&mut ArrowFunctionBody`] to an [`&mut Expression`].
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if not convertible.
|
||||
///
|
||||
/// [`&mut ArrowFunctionBody`]: ArrowFunctionBody
|
||||
/// [`&mut Expression`]: Expression
|
||||
#[inline]
|
||||
pub fn to_expression_mut(&mut self) -> &mut Expression<'a> {
|
||||
self.as_expression_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Expression<'a> {
|
||||
/// Convert an [`&Expression`] to an [`&ArrowFunctionBody`].
|
||||
///
|
||||
/// [`&Expression`]: Expression
|
||||
/// [`&ArrowFunctionBody`]: ArrowFunctionBody
|
||||
#[inline]
|
||||
pub fn as_arrow_function_body(&self) -> &ArrowFunctionBody<'a> {
|
||||
// SAFETY: Transmute is safe because discriminants + types are identical between
|
||||
// `parent` and `child` for the shared variants
|
||||
unsafe { NonNull::from_ref(self).cast::<ArrowFunctionBody>().as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<ArrowFunctionBody<'a>> for Expression<'a> {
|
||||
type Error = ();
|
||||
|
||||
/// Convert an [`ArrowFunctionBody`] to an [`Expression`].
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns `Err` if not convertible.
|
||||
#[inline]
|
||||
fn try_from(value: ArrowFunctionBody<'a>) -> Result<Self, Self::Error> {
|
||||
// Compiler should implement this as a check of discriminant and then zero-cost transmute,
|
||||
// as discriminants for `parent` and `child` are aligned
|
||||
match value {
|
||||
ArrowFunctionBody::BooleanLiteral(o) => Ok(Expression::BooleanLiteral(o)),
|
||||
ArrowFunctionBody::NullLiteral(o) => Ok(Expression::NullLiteral(o)),
|
||||
ArrowFunctionBody::NumericLiteral(o) => Ok(Expression::NumericLiteral(o)),
|
||||
ArrowFunctionBody::BigIntLiteral(o) => Ok(Expression::BigIntLiteral(o)),
|
||||
ArrowFunctionBody::RegExpLiteral(o) => Ok(Expression::RegExpLiteral(o)),
|
||||
ArrowFunctionBody::StringLiteral(o) => Ok(Expression::StringLiteral(o)),
|
||||
ArrowFunctionBody::TemplateLiteral(o) => Ok(Expression::TemplateLiteral(o)),
|
||||
ArrowFunctionBody::Identifier(o) => Ok(Expression::Identifier(o)),
|
||||
ArrowFunctionBody::Super(o) => Ok(Expression::Super(o)),
|
||||
ArrowFunctionBody::ArrayExpression(o) => Ok(Expression::ArrayExpression(o)),
|
||||
ArrowFunctionBody::ArrowFunctionExpression(o) => {
|
||||
Ok(Expression::ArrowFunctionExpression(o))
|
||||
}
|
||||
ArrowFunctionBody::AssignmentExpression(o) => Ok(Expression::AssignmentExpression(o)),
|
||||
ArrowFunctionBody::AwaitExpression(o) => Ok(Expression::AwaitExpression(o)),
|
||||
ArrowFunctionBody::BinaryExpression(o) => Ok(Expression::BinaryExpression(o)),
|
||||
ArrowFunctionBody::CallExpression(o) => Ok(Expression::CallExpression(o)),
|
||||
ArrowFunctionBody::ChainExpression(o) => Ok(Expression::ChainExpression(o)),
|
||||
ArrowFunctionBody::ClassExpression(o) => Ok(Expression::ClassExpression(o)),
|
||||
ArrowFunctionBody::ConditionalExpression(o) => Ok(Expression::ConditionalExpression(o)),
|
||||
ArrowFunctionBody::FunctionExpression(o) => Ok(Expression::FunctionExpression(o)),
|
||||
ArrowFunctionBody::ImportExpression(o) => Ok(Expression::ImportExpression(o)),
|
||||
ArrowFunctionBody::LogicalExpression(o) => Ok(Expression::LogicalExpression(o)),
|
||||
ArrowFunctionBody::NewExpression(o) => Ok(Expression::NewExpression(o)),
|
||||
ArrowFunctionBody::ObjectExpression(o) => Ok(Expression::ObjectExpression(o)),
|
||||
ArrowFunctionBody::ParenthesizedExpression(o) => {
|
||||
Ok(Expression::ParenthesizedExpression(o))
|
||||
}
|
||||
ArrowFunctionBody::SequenceExpression(o) => Ok(Expression::SequenceExpression(o)),
|
||||
ArrowFunctionBody::TaggedTemplateExpression(o) => {
|
||||
Ok(Expression::TaggedTemplateExpression(o))
|
||||
}
|
||||
ArrowFunctionBody::ThisExpression(o) => Ok(Expression::ThisExpression(o)),
|
||||
ArrowFunctionBody::UnaryExpression(o) => Ok(Expression::UnaryExpression(o)),
|
||||
ArrowFunctionBody::UpdateExpression(o) => Ok(Expression::UpdateExpression(o)),
|
||||
ArrowFunctionBody::YieldExpression(o) => Ok(Expression::YieldExpression(o)),
|
||||
ArrowFunctionBody::PrivateInExpression(o) => Ok(Expression::PrivateInExpression(o)),
|
||||
ArrowFunctionBody::ImportMeta(o) => Ok(Expression::ImportMeta(o)),
|
||||
ArrowFunctionBody::NewTarget(o) => Ok(Expression::NewTarget(o)),
|
||||
ArrowFunctionBody::JSXElement(o) => Ok(Expression::JSXElement(o)),
|
||||
ArrowFunctionBody::JSXFragment(o) => Ok(Expression::JSXFragment(o)),
|
||||
ArrowFunctionBody::TSAsExpression(o) => Ok(Expression::TSAsExpression(o)),
|
||||
ArrowFunctionBody::TSSatisfiesExpression(o) => Ok(Expression::TSSatisfiesExpression(o)),
|
||||
ArrowFunctionBody::TSTypeAssertion(o) => Ok(Expression::TSTypeAssertion(o)),
|
||||
ArrowFunctionBody::TSNonNullExpression(o) => Ok(Expression::TSNonNullExpression(o)),
|
||||
ArrowFunctionBody::TSInstantiationExpression(o) => {
|
||||
Ok(Expression::TSInstantiationExpression(o))
|
||||
}
|
||||
ArrowFunctionBody::V8IntrinsicExpression(o) => Ok(Expression::V8IntrinsicExpression(o)),
|
||||
ArrowFunctionBody::ComputedMemberExpression(o) => {
|
||||
Ok(Expression::ComputedMemberExpression(o))
|
||||
}
|
||||
ArrowFunctionBody::StaticMemberExpression(o) => {
|
||||
Ok(Expression::StaticMemberExpression(o))
|
||||
}
|
||||
ArrowFunctionBody::PrivateFieldExpression(o) => {
|
||||
Ok(Expression::PrivateFieldExpression(o))
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Expression<'a>> for ArrowFunctionBody<'a> {
|
||||
/// Convert an [`Expression`] to an [`ArrowFunctionBody`].
|
||||
#[inline]
|
||||
fn from(value: Expression<'a>) -> Self {
|
||||
// Compiler should implement this as zero-cost transmute as discriminants
|
||||
// for `child` and `parent` are aligned
|
||||
match value {
|
||||
Expression::BooleanLiteral(o) => ArrowFunctionBody::BooleanLiteral(o),
|
||||
Expression::NullLiteral(o) => ArrowFunctionBody::NullLiteral(o),
|
||||
Expression::NumericLiteral(o) => ArrowFunctionBody::NumericLiteral(o),
|
||||
Expression::BigIntLiteral(o) => ArrowFunctionBody::BigIntLiteral(o),
|
||||
Expression::RegExpLiteral(o) => ArrowFunctionBody::RegExpLiteral(o),
|
||||
Expression::StringLiteral(o) => ArrowFunctionBody::StringLiteral(o),
|
||||
Expression::TemplateLiteral(o) => ArrowFunctionBody::TemplateLiteral(o),
|
||||
Expression::Identifier(o) => ArrowFunctionBody::Identifier(o),
|
||||
Expression::Super(o) => ArrowFunctionBody::Super(o),
|
||||
Expression::ArrayExpression(o) => ArrowFunctionBody::ArrayExpression(o),
|
||||
Expression::ArrowFunctionExpression(o) => ArrowFunctionBody::ArrowFunctionExpression(o),
|
||||
Expression::AssignmentExpression(o) => ArrowFunctionBody::AssignmentExpression(o),
|
||||
Expression::AwaitExpression(o) => ArrowFunctionBody::AwaitExpression(o),
|
||||
Expression::BinaryExpression(o) => ArrowFunctionBody::BinaryExpression(o),
|
||||
Expression::CallExpression(o) => ArrowFunctionBody::CallExpression(o),
|
||||
Expression::ChainExpression(o) => ArrowFunctionBody::ChainExpression(o),
|
||||
Expression::ClassExpression(o) => ArrowFunctionBody::ClassExpression(o),
|
||||
Expression::ConditionalExpression(o) => ArrowFunctionBody::ConditionalExpression(o),
|
||||
Expression::FunctionExpression(o) => ArrowFunctionBody::FunctionExpression(o),
|
||||
Expression::ImportExpression(o) => ArrowFunctionBody::ImportExpression(o),
|
||||
Expression::LogicalExpression(o) => ArrowFunctionBody::LogicalExpression(o),
|
||||
Expression::NewExpression(o) => ArrowFunctionBody::NewExpression(o),
|
||||
Expression::ObjectExpression(o) => ArrowFunctionBody::ObjectExpression(o),
|
||||
Expression::ParenthesizedExpression(o) => ArrowFunctionBody::ParenthesizedExpression(o),
|
||||
Expression::SequenceExpression(o) => ArrowFunctionBody::SequenceExpression(o),
|
||||
Expression::TaggedTemplateExpression(o) => {
|
||||
ArrowFunctionBody::TaggedTemplateExpression(o)
|
||||
}
|
||||
Expression::ThisExpression(o) => ArrowFunctionBody::ThisExpression(o),
|
||||
Expression::UnaryExpression(o) => ArrowFunctionBody::UnaryExpression(o),
|
||||
Expression::UpdateExpression(o) => ArrowFunctionBody::UpdateExpression(o),
|
||||
Expression::YieldExpression(o) => ArrowFunctionBody::YieldExpression(o),
|
||||
Expression::PrivateInExpression(o) => ArrowFunctionBody::PrivateInExpression(o),
|
||||
Expression::ImportMeta(o) => ArrowFunctionBody::ImportMeta(o),
|
||||
Expression::NewTarget(o) => ArrowFunctionBody::NewTarget(o),
|
||||
Expression::JSXElement(o) => ArrowFunctionBody::JSXElement(o),
|
||||
Expression::JSXFragment(o) => ArrowFunctionBody::JSXFragment(o),
|
||||
Expression::TSAsExpression(o) => ArrowFunctionBody::TSAsExpression(o),
|
||||
Expression::TSSatisfiesExpression(o) => ArrowFunctionBody::TSSatisfiesExpression(o),
|
||||
Expression::TSTypeAssertion(o) => ArrowFunctionBody::TSTypeAssertion(o),
|
||||
Expression::TSNonNullExpression(o) => ArrowFunctionBody::TSNonNullExpression(o),
|
||||
Expression::TSInstantiationExpression(o) => {
|
||||
ArrowFunctionBody::TSInstantiationExpression(o)
|
||||
}
|
||||
Expression::V8IntrinsicExpression(o) => ArrowFunctionBody::V8IntrinsicExpression(o),
|
||||
Expression::ComputedMemberExpression(o) => {
|
||||
ArrowFunctionBody::ComputedMemberExpression(o)
|
||||
}
|
||||
Expression::StaticMemberExpression(o) => ArrowFunctionBody::StaticMemberExpression(o),
|
||||
Expression::PrivateFieldExpression(o) => ArrowFunctionBody::PrivateFieldExpression(o),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ArrowFunctionBody<'a> {
|
||||
/// Return if an [`ArrowFunctionBody`] is a [`MemberExpression`].
|
||||
#[inline]
|
||||
pub fn is_member_expression(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::ComputedMemberExpression(_)
|
||||
| Self::StaticMemberExpression(_)
|
||||
| Self::PrivateFieldExpression(_)
|
||||
)
|
||||
}
|
||||
|
||||
/// Convert an [`ArrowFunctionBody`] to a [`MemberExpression`].
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if not convertible.
|
||||
#[inline]
|
||||
pub fn into_member_expression(self) -> MemberExpression<'a> {
|
||||
MemberExpression::try_from(self).unwrap()
|
||||
}
|
||||
|
||||
/// Convert an [`&ArrowFunctionBody`] to a [`&MemberExpression`].
|
||||
///
|
||||
/// [`&ArrowFunctionBody`]: ArrowFunctionBody
|
||||
/// [`&MemberExpression`]: MemberExpression
|
||||
#[inline]
|
||||
pub fn as_member_expression(&self) -> Option<&MemberExpression<'a>> {
|
||||
if self.is_member_expression() {
|
||||
// SAFETY: Transmute is safe because discriminants + types are identical between
|
||||
// `parent` and `child` for the shared variants
|
||||
Some(unsafe { NonNull::from_ref(self).cast::<MemberExpression>().as_ref() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an [`&mut ArrowFunctionBody`] to a [`&mut MemberExpression`].
|
||||
///
|
||||
/// [`&mut ArrowFunctionBody`]: ArrowFunctionBody
|
||||
/// [`&mut MemberExpression`]: MemberExpression
|
||||
#[inline]
|
||||
pub fn as_member_expression_mut(&mut self) -> Option<&mut MemberExpression<'a>> {
|
||||
if self.is_member_expression() {
|
||||
// SAFETY: Transmute is safe because discriminants + types are identical between
|
||||
// `parent` and `child` for the shared variants
|
||||
Some(unsafe { NonNull::from_mut(self).cast::<MemberExpression>().as_mut() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an [`&ArrowFunctionBody`] to a [`&MemberExpression`].
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if not convertible.
|
||||
///
|
||||
/// [`&ArrowFunctionBody`]: ArrowFunctionBody
|
||||
/// [`&MemberExpression`]: MemberExpression
|
||||
#[inline]
|
||||
pub fn to_member_expression(&self) -> &MemberExpression<'a> {
|
||||
self.as_member_expression().unwrap()
|
||||
}
|
||||
|
||||
/// Convert an [`&mut ArrowFunctionBody`] to a [`&mut MemberExpression`].
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if not convertible.
|
||||
///
|
||||
/// [`&mut ArrowFunctionBody`]: ArrowFunctionBody
|
||||
/// [`&mut MemberExpression`]: MemberExpression
|
||||
#[inline]
|
||||
pub fn to_member_expression_mut(&mut self) -> &mut MemberExpression<'a> {
|
||||
self.as_member_expression_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MemberExpression<'a> {
|
||||
/// Convert a [`&MemberExpression`] to an [`&ArrowFunctionBody`].
|
||||
///
|
||||
/// [`&MemberExpression`]: MemberExpression
|
||||
/// [`&ArrowFunctionBody`]: ArrowFunctionBody
|
||||
#[inline]
|
||||
pub fn as_arrow_function_body(&self) -> &ArrowFunctionBody<'a> {
|
||||
// SAFETY: Transmute is safe because discriminants + types are identical between
|
||||
// `parent` and `child` for the shared variants
|
||||
unsafe { NonNull::from_ref(self).cast::<ArrowFunctionBody>().as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<ArrowFunctionBody<'a>> for MemberExpression<'a> {
|
||||
type Error = ();
|
||||
|
||||
/// Convert an [`ArrowFunctionBody`] to a [`MemberExpression`].
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns `Err` if not convertible.
|
||||
#[inline]
|
||||
fn try_from(value: ArrowFunctionBody<'a>) -> Result<Self, Self::Error> {
|
||||
// Compiler should implement this as a check of discriminant and then zero-cost transmute,
|
||||
// as discriminants for `parent` and `child` are aligned
|
||||
match value {
|
||||
ArrowFunctionBody::ComputedMemberExpression(o) => {
|
||||
Ok(MemberExpression::ComputedMemberExpression(o))
|
||||
}
|
||||
ArrowFunctionBody::StaticMemberExpression(o) => {
|
||||
Ok(MemberExpression::StaticMemberExpression(o))
|
||||
}
|
||||
ArrowFunctionBody::PrivateFieldExpression(o) => {
|
||||
Ok(MemberExpression::PrivateFieldExpression(o))
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<MemberExpression<'a>> for ArrowFunctionBody<'a> {
|
||||
/// Convert a [`MemberExpression`] to an [`ArrowFunctionBody`].
|
||||
#[inline]
|
||||
fn from(value: MemberExpression<'a>) -> Self {
|
||||
// Compiler should implement this as zero-cost transmute as discriminants
|
||||
// for `child` and `parent` are aligned
|
||||
match value {
|
||||
MemberExpression::ComputedMemberExpression(o) => {
|
||||
ArrowFunctionBody::ComputedMemberExpression(o)
|
||||
}
|
||||
MemberExpression::StaticMemberExpression(o) => {
|
||||
ArrowFunctionBody::StaticMemberExpression(o)
|
||||
}
|
||||
MemberExpression::PrivateFieldExpression(o) => {
|
||||
ArrowFunctionBody::PrivateFieldExpression(o)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExportDefaultDeclarationKind<'a> {
|
||||
/// Return if an [`ExportDefaultDeclarationKind`] is an [`Expression`].
|
||||
#[inline]
|
||||
|
||||
@@ -811,31 +811,14 @@ impl ESTree for ExportAllDeclarationWithClause<'_, '_> {
|
||||
// Misc
|
||||
// ----------------------------------------
|
||||
|
||||
/// Serializer for `body` field of `ArrowFunctionExpression`.
|
||||
///
|
||||
/// Serialize as either an expression (if `expression` property is set),
|
||||
/// or a `BlockStatement` (if it's not).
|
||||
/// Serializer for the derived `expression` field of [`ArrowFunctionExpression`].
|
||||
#[ast_meta]
|
||||
#[estree(
|
||||
ts_type = "FunctionBody | Expression",
|
||||
raw_deser = "
|
||||
let body = DESER[Box<FunctionBody>](POS_OFFSET.body);
|
||||
if (THIS.expression === true) {
|
||||
body = body.body[0].expression;
|
||||
if (PARENT) body.parent = parent;
|
||||
}
|
||||
body
|
||||
"
|
||||
)]
|
||||
pub struct ArrowFunctionExpressionBody<'a>(pub &'a ArrowFunctionExpression<'a>);
|
||||
#[estree(ts_type = "boolean", raw_deser = "uint8[POS_OFFSET.body] !== 64", raw_deser_inline)]
|
||||
pub struct ArrowFunctionExpressionExpression<'a>(pub &'a ArrowFunctionExpression<'a>);
|
||||
|
||||
impl ESTree for ArrowFunctionExpressionBody<'_> {
|
||||
impl ESTree for ArrowFunctionExpressionExpression<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(expression) = self.0.get_expression() {
|
||||
expression.serialize(serializer);
|
||||
} else {
|
||||
self.0.body.serialize(serializer);
|
||||
}
|
||||
self.0.is_expression().serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,122 +8,123 @@ use crate::ast::{EnumDetails, EnumVariant, InheritedEnum};
|
||||
pub static ENUMS: phf::Map<&'static str, EnumDetails> = ::phf::Map {
|
||||
key: 16287231350648472473,
|
||||
disps: &[
|
||||
(0, 17),
|
||||
(0, 3),
|
||||
(0, 0),
|
||||
(0, 9),
|
||||
(0, 12),
|
||||
(0, 2),
|
||||
(0, 11),
|
||||
(0, 4),
|
||||
(0, 1),
|
||||
(0, 0),
|
||||
(0, 7),
|
||||
(0, 37),
|
||||
(0, 2),
|
||||
(0, 64),
|
||||
(0, 30),
|
||||
(0, 5),
|
||||
(1, 1),
|
||||
(0, 4),
|
||||
(0, 0),
|
||||
(0, 20),
|
||||
(0, 3),
|
||||
(1, 32),
|
||||
(0, 19),
|
||||
(0, 72),
|
||||
(0, 12),
|
||||
(0, 24),
|
||||
(1, 82),
|
||||
(0, 0),
|
||||
(1, 81),
|
||||
(0, 52),
|
||||
(0, 0),
|
||||
(0, 55),
|
||||
(0, 9),
|
||||
(2, 34),
|
||||
(0, 41),
|
||||
(0, 30),
|
||||
(1, 30),
|
||||
(0, 0),
|
||||
(0, 2),
|
||||
(0, 60),
|
||||
(0, 14),
|
||||
(0, 45),
|
||||
(2, 19),
|
||||
(10, 57),
|
||||
(0, 31),
|
||||
(0, 56),
|
||||
(0, 0),
|
||||
(7, 6),
|
||||
(1, 24),
|
||||
(6, 16),
|
||||
(0, 4),
|
||||
],
|
||||
entries: &[
|
||||
("JSXAttributeValue", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("JSXAttributeName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ImportOrExportKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("JSXElementName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("UpdateOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("AssignmentTargetProperty", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("AssignmentTarget", EnumDetails { is_fieldless: false, inherits: &[3, 4] }),
|
||||
("ErrorSeverity", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSEnumMemberName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSModuleReference", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("Declaration", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("Term", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ImportAttributeKey", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("FormalParameterKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("LanguageVariant", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSLiteral", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("WithClauseKeyword", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("FileExtension", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSType", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("CharacterClassEscapeKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("Language", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ModuleExportName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ArrayExpressionElement", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("ChainElement", EnumDetails { is_fieldless: false, inherits: &[1] }),
|
||||
("TSMappedTypeModifierOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("MethodDefinitionType", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSModuleDeclarationKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("JSXExpression", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("ObjectPropertyKind", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("BindingPattern", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("Statement", EnumDetails { is_fieldless: false, inherits: &[5, 6] }),
|
||||
("AccessorPropertyType", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("CommentKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSTupleElement", EnumDetails { is_fieldless: false, inherits: &[7] }),
|
||||
("ForStatementInit", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("TSLiteral", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("FunctionType", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("Argument", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("Expression", EnumDetails { is_fieldless: false, inherits: &[1] }),
|
||||
("ImportDeclarationSpecifier", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("CharacterClassContents", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("LogicalOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("CommentPosition", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("PropertyKey", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("ModuleDeclaration", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSMethodSignatureKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSTupleElement", EnumDetails { is_fieldless: false, inherits: &[7] }),
|
||||
("ExportLocalName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("PropertyKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("AssignmentTargetPattern", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSModuleDeclarationBody", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("LookAroundAssertionKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSTypeQueryExprName", EnumDetails { is_fieldless: false, inherits: &[8] }),
|
||||
("ImportPhase", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ClassType", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("CharacterKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("BinaryOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSImportTypeQualifier", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("JSXAttributeItem", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("PropertyDefinitionType", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("MethodDefinitionKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("JSXMemberExpressionObject", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("UpdateOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("CommentKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("MethodDefinitionType", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("Statement", EnumDetails { is_fieldless: false, inherits: &[5, 6] }),
|
||||
("TSEnumMemberName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("LanguageVariant", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("BigintBase", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("BindingPattern", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("CommentPosition", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSTypeName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("PropertyKey", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("FileExtension", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("JSXExpression", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("AssignmentTarget", EnumDetails { is_fieldless: false, inherits: &[3, 4] }),
|
||||
("LogicalOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("CharacterClassEscapeKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ModuleExportName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("Declaration", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ImportDeclarationSpecifier", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ExportImportName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("Expression", EnumDetails { is_fieldless: false, inherits: &[1] }),
|
||||
("JSXElementName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSModuleDeclarationName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("CharacterClassContentsKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ArrayExpressionElement", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("TSType", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSImportTypeQualifier", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ErrorSeverity", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("BinaryOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ImportAttributeKey", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSAccessibility", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("JSXAttributeName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("PropertyDefinitionType", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("AssignmentOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ImportPhase", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSTypeQueryExprName", EnumDetails { is_fieldless: false, inherits: &[8] }),
|
||||
("JSXChild", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("JSXMemberExpressionObject", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ForStatementInit", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("MemberExpression", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("Term", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ImportOrExportKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("PropertyKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("MethodDefinitionKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ClassType", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSMethodSignatureKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("AssignmentTargetProperty", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("FormalParameterKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ClassElement", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ModuleDeclaration", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("JSXAttributeItem", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ImportImportName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("Argument", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("CharacterKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSTypePredicateName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSModuleDeclarationKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ExportDefaultDeclarationKind", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("TSTypeOperatorOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("Language", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("AccessorPropertyType", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("BoundaryAssertionKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSMappedTypeModifierOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ObjectPropertyKind", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("CommentContent", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("SimpleAssignmentTarget", EnumDetails { is_fieldless: false, inherits: &[1] }),
|
||||
("WithClauseKeyword", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ExportExportName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("VariableDeclarationKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("UnaryOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("JSXChild", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSSignature", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSTypePredicateName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ExportDefaultDeclarationKind", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("ExportImportName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ForStatementLeft", EnumDetails { is_fieldless: false, inherits: &[2] }),
|
||||
("BigintBase", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("BoundaryAssertionKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("CommentContent", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSTypeOperatorOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("AssignmentOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("TSModuleDeclarationBody", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("ClassElement", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("AssignmentTargetPattern", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSTypeName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("SimpleAssignmentTarget", EnumDetails { is_fieldless: false, inherits: &[1] }),
|
||||
("ModuleKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("AssignmentTargetMaybeDefault", EnumDetails { is_fieldless: false, inherits: &[2] }),
|
||||
("TSAccessibility", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("MemberExpression", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSModuleReference", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("NumberBase", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("ImportImportName", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("CharacterClassContentsKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("AssignmentTargetMaybeDefault", EnumDetails { is_fieldless: false, inherits: &[2] }),
|
||||
("ArrowFunctionBody", EnumDetails { is_fieldless: false, inherits: &[0] }),
|
||||
("ForStatementLeft", EnumDetails { is_fieldless: false, inherits: &[2] }),
|
||||
("ModuleKind", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
("CharacterClassContents", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("JSXAttributeValue", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("TSSignature", EnumDetails { is_fieldless: false, inherits: &[] }),
|
||||
("UnaryOperator", EnumDetails { is_fieldless: true, inherits: &[] }),
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -420,7 +420,7 @@ pub static STRUCTS: phf::Map<&'static str, StructDetails> = ::phf::Map {
|
||||
(
|
||||
"ArrowFunctionExpression",
|
||||
StructDetails {
|
||||
field_order: Some(&[1, 0, 7, 8, 3, 4, 5, 6, 2, 9, 10]),
|
||||
field_order: Some(&[1, 0, 7, 3, 4, 5, 6, 2, 8, 9]),
|
||||
is_node: true,
|
||||
is_transparent: false,
|
||||
},
|
||||
|
||||
@@ -501,6 +501,11 @@ pub trait Visit<'a>: Sized {
|
||||
walk_function_body(self, it);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_arrow_function_body(&mut self, it: &ArrowFunctionBody<'a>) {
|
||||
walk_arrow_function_body(self, it);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_arrow_function_expression(&mut self, it: &ArrowFunctionExpression<'a>) {
|
||||
walk_arrow_function_expression(self, it);
|
||||
@@ -2533,6 +2538,15 @@ pub mod walk {
|
||||
visitor.leave_node(kind);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn walk_arrow_function_body<'a, V: Visit<'a>>(visitor: &mut V, it: &ArrowFunctionBody<'a>) {
|
||||
// No `AstKind` for this type
|
||||
match it {
|
||||
ArrowFunctionBody::FunctionBody(it) => visitor.visit_function_body(it),
|
||||
match_expression!(ArrowFunctionBody) => visitor.visit_expression(it.to_expression()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn walk_arrow_function_expression<'a, V: Visit<'a>>(
|
||||
visitor: &mut V,
|
||||
@@ -2558,7 +2572,7 @@ pub mod walk {
|
||||
if let Some(return_type) = &it.return_type {
|
||||
visitor.visit_ts_type_annotation(return_type);
|
||||
}
|
||||
visitor.visit_function_body(&it.body);
|
||||
visitor.visit_arrow_function_body(&it.body);
|
||||
visitor.leave_scope();
|
||||
visitor.leave_node(kind);
|
||||
}
|
||||
|
||||
@@ -528,6 +528,11 @@ pub trait VisitJs<'a>: Sized {
|
||||
walk_function_body(self, it);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_arrow_function_body(&mut self, it: &ArrowFunctionBody<'a>) {
|
||||
walk_arrow_function_body(self, it);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_arrow_function_expression(&mut self, it: &ArrowFunctionExpression<'a>) {
|
||||
walk_arrow_function_expression(self, it);
|
||||
@@ -2188,6 +2193,18 @@ pub mod walk_js {
|
||||
visitor.leave_node(kind);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn walk_arrow_function_body<'a, V: VisitJs<'a>>(
|
||||
visitor: &mut V,
|
||||
it: &ArrowFunctionBody<'a>,
|
||||
) {
|
||||
// No `AstKind` for this type
|
||||
match it {
|
||||
ArrowFunctionBody::FunctionBody(it) => visitor.visit_function_body(it),
|
||||
match_expression!(ArrowFunctionBody) => visitor.visit_expression(it.to_expression()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn walk_arrow_function_expression<'a, V: VisitJs<'a>>(
|
||||
visitor: &mut V,
|
||||
@@ -2207,7 +2224,7 @@ pub mod walk_js {
|
||||
);
|
||||
visitor.visit_span(&it.span);
|
||||
visitor.visit_formal_parameters(&it.params);
|
||||
visitor.visit_function_body(&it.body);
|
||||
visitor.visit_arrow_function_body(&it.body);
|
||||
visitor.leave_scope();
|
||||
visitor.leave_node(kind);
|
||||
}
|
||||
|
||||
@@ -520,6 +520,11 @@ pub trait VisitJsMut<'a>: Sized {
|
||||
walk_function_body(self, it);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_arrow_function_body(&mut self, it: &mut ArrowFunctionBody<'a>) {
|
||||
walk_arrow_function_body(self, it);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_arrow_function_expression(&mut self, it: &mut ArrowFunctionExpression<'a>) {
|
||||
walk_arrow_function_expression(self, it);
|
||||
@@ -2282,6 +2287,20 @@ pub mod walk_js_mut {
|
||||
visitor.leave_node(kind);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn walk_arrow_function_body<'a, V: VisitJsMut<'a>>(
|
||||
visitor: &mut V,
|
||||
it: &mut ArrowFunctionBody<'a>,
|
||||
) {
|
||||
// No `AstType` for this type
|
||||
match it {
|
||||
ArrowFunctionBody::FunctionBody(it) => visitor.visit_function_body(it),
|
||||
match_expression!(ArrowFunctionBody) => {
|
||||
visitor.visit_expression(it.to_expression_mut())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn walk_arrow_function_expression<'a, V: VisitJsMut<'a>>(
|
||||
visitor: &mut V,
|
||||
@@ -2301,7 +2320,7 @@ pub mod walk_js_mut {
|
||||
);
|
||||
visitor.visit_span(&mut it.span);
|
||||
visitor.visit_formal_parameters(&mut it.params);
|
||||
visitor.visit_function_body(&mut it.body);
|
||||
visitor.visit_arrow_function_body(&mut it.body);
|
||||
visitor.leave_scope();
|
||||
visitor.leave_node(kind);
|
||||
}
|
||||
|
||||
@@ -493,6 +493,11 @@ pub trait VisitMut<'a>: Sized {
|
||||
walk_function_body(self, it);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_arrow_function_body(&mut self, it: &mut ArrowFunctionBody<'a>) {
|
||||
walk_arrow_function_body(self, it);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_arrow_function_expression(&mut self, it: &mut ArrowFunctionExpression<'a>) {
|
||||
walk_arrow_function_expression(self, it);
|
||||
@@ -2616,6 +2621,20 @@ pub mod walk_mut {
|
||||
visitor.leave_node(kind);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn walk_arrow_function_body<'a, V: VisitMut<'a>>(
|
||||
visitor: &mut V,
|
||||
it: &mut ArrowFunctionBody<'a>,
|
||||
) {
|
||||
// No `AstType` for this type
|
||||
match it {
|
||||
ArrowFunctionBody::FunctionBody(it) => visitor.visit_function_body(it),
|
||||
match_expression!(ArrowFunctionBody) => {
|
||||
visitor.visit_expression(it.to_expression_mut())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn walk_arrow_function_expression<'a, V: VisitMut<'a>>(
|
||||
visitor: &mut V,
|
||||
@@ -2641,7 +2660,7 @@ pub mod walk_mut {
|
||||
if let Some(return_type) = &mut it.return_type {
|
||||
visitor.visit_ts_type_annotation(return_type);
|
||||
}
|
||||
visitor.visit_function_body(&mut it.body);
|
||||
visitor.visit_arrow_function_body(&mut it.body);
|
||||
visitor.leave_scope();
|
||||
visitor.leave_node(kind);
|
||||
}
|
||||
|
||||
@@ -1855,13 +1855,15 @@ impl GenExpr for ArrowFunctionExpression<'_> {
|
||||
p.print_soft_space();
|
||||
p.print_str("=>");
|
||||
p.print_soft_space();
|
||||
if self.expression {
|
||||
if let Some(Statement::ExpressionStatement(stmt)) = &self.body.statements.first() {
|
||||
match &self.body {
|
||||
ArrowFunctionBody::FunctionBody(b) => {
|
||||
b.print(p, body_ctx);
|
||||
}
|
||||
expression @ match_expression!(ArrowFunctionBody) => {
|
||||
let expression = expression.to_expression();
|
||||
p.start_of_arrow_expr = p.code_len();
|
||||
stmt.expression.print_expr(p, Precedence::Comma, body_ctx);
|
||||
expression.print_expr(p, Precedence::Comma, body_ctx);
|
||||
}
|
||||
} else {
|
||||
self.body.print(p, body_ctx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -550,11 +550,22 @@ fn iife_call_may_have_side_effects<'a>(
|
||||
call: &CallExpression<'a>,
|
||||
ctx: &impl MayHaveSideEffectsContext<'a>,
|
||||
) -> Option<bool> {
|
||||
let (params, body) = match &call.callee {
|
||||
let (params, body_may_have_side_effects) = match &call.callee {
|
||||
Expression::FunctionExpression(f) if !f.r#async && !f.generator => {
|
||||
(&f.params, f.body.as_deref()?)
|
||||
let body = f.body.as_deref()?;
|
||||
(&f.params, body.statements.iter().any(|stmt| stmt.may_have_side_effects(ctx)))
|
||||
}
|
||||
Expression::ArrowFunctionExpression(f) if !f.r#async => {
|
||||
let body_may_have_side_effects = match &f.body {
|
||||
ArrowFunctionBody::FunctionBody(b) => {
|
||||
b.statements.iter().any(|stmt| stmt.may_have_side_effects(ctx))
|
||||
}
|
||||
match_expression!(ArrowFunctionBody) => {
|
||||
f.get_expression().unwrap().may_have_side_effects(ctx)
|
||||
}
|
||||
};
|
||||
(&f.params, body_may_have_side_effects)
|
||||
}
|
||||
Expression::ArrowFunctionExpression(f) if !f.r#async => (&f.params, &*f.body),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@@ -570,7 +581,7 @@ fn iife_call_may_have_side_effects<'a>(
|
||||
return Some(true);
|
||||
}
|
||||
|
||||
Some(body.statements.iter().any(|stmt| stmt.may_have_side_effects(ctx)))
|
||||
Some(body_may_have_side_effects)
|
||||
}
|
||||
|
||||
// `PF` in <https://github.com/rollup/rollup/blob/master/src/ast/nodes/shared/knownGlobals.ts>
|
||||
|
||||
@@ -4846,17 +4846,41 @@ impl<'a> AstNode<'a, FunctionBody<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AstNode<'a, ArrowFunctionBody<'a>> {
|
||||
#[inline]
|
||||
pub fn as_ast_nodes(&self) -> &AstNodes<'a> {
|
||||
let parent = self.parent;
|
||||
let node = match self.inner {
|
||||
ArrowFunctionBody::FunctionBody(s) => {
|
||||
AstNodes::FunctionBody(self.allocator.alloc(AstNode {
|
||||
inner: s.as_ref(),
|
||||
parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
}))
|
||||
}
|
||||
it @ match_expression!(ArrowFunctionBody) => {
|
||||
return self
|
||||
.allocator
|
||||
.alloc(AstNode {
|
||||
inner: it.to_expression(),
|
||||
parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
})
|
||||
.as_ast_nodes();
|
||||
}
|
||||
};
|
||||
self.allocator.alloc(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AstNode<'a, ArrowFunctionExpression<'a>> {
|
||||
#[inline]
|
||||
pub fn node_id(&self) -> NodeId {
|
||||
self.inner.node_id()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn expression(&self) -> bool {
|
||||
self.inner.expression
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn r#async(&self) -> bool {
|
||||
self.inner.r#async
|
||||
@@ -4906,10 +4930,10 @@ impl<'a> AstNode<'a, ArrowFunctionExpression<'a>> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn body(&self) -> &AstNode<'a, FunctionBody<'a>> {
|
||||
pub fn body(&self) -> &AstNode<'a, ArrowFunctionBody<'a>> {
|
||||
let following_span_start = self.following_span_start;
|
||||
self.allocator.alloc(AstNode {
|
||||
inner: self.inner.body.as_ref(),
|
||||
inner: &self.inner.body,
|
||||
allocator: self.allocator,
|
||||
parent: AstNodes::ArrowFunctionExpression(transmute_self(self)),
|
||||
following_span_start,
|
||||
|
||||
@@ -2473,6 +2473,37 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, FunctionBody<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArrowFunctionBody<'a>> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let allocator = self.allocator;
|
||||
let parent = self.parent;
|
||||
match self.inner {
|
||||
ArrowFunctionBody::FunctionBody(inner) => {
|
||||
allocator
|
||||
.alloc(AstNode::<FunctionBody> {
|
||||
inner,
|
||||
parent,
|
||||
allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
})
|
||||
.fmt(f);
|
||||
}
|
||||
it @ match_expression!(ArrowFunctionBody) => {
|
||||
let inner = it.to_expression();
|
||||
allocator
|
||||
.alloc(AstNode::<'a, Expression> {
|
||||
inner,
|
||||
parent,
|
||||
allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
})
|
||||
.fmt(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArrowFunctionExpression<'a>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let is_suppressed = f.comments().is_suppressed(self.span().start);
|
||||
|
||||
@@ -139,17 +139,17 @@ impl<T: GetSpan> AstNode<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AstNode<'a, ExpressionStatement<'a>> {
|
||||
/// Check if this ExpressionStatement is the body of an arrow function expression
|
||||
///
|
||||
/// Example:
|
||||
/// `() => expression;`
|
||||
/// ^^^^^^^^^^ This ExpressionStatement is the body of an arrow function
|
||||
///
|
||||
/// `() => { return expression; }`
|
||||
/// ^^^^^^^^^^^^^^^^^^^^ This ExpressionStatement is NOT the body of an arrow function
|
||||
pub fn is_arrow_function_body(&self) -> bool {
|
||||
matches!(self.parent().parent(), AstNodes::ArrowFunctionExpression(arrow) if arrow.expression)
|
||||
impl<'a> AstNode<'a, ArrowFunctionBody<'a>> {
|
||||
/// Returns the concise expression body as an [`AstNode`].
|
||||
pub fn as_expression(&self) -> Option<&AstNode<'a, Expression<'a>>> {
|
||||
self.allocator
|
||||
.alloc(self.inner.as_expression().map(|inner| AstNode {
|
||||
inner,
|
||||
parent: self.parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
}))
|
||||
.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,10 +83,7 @@ impl NeedsParentheses<'_> for AstNode<'_, IdentifierReference<'_>> {
|
||||
}
|
||||
|
||||
// Needs at least one `satisfies`/`as` wrapper, with a statement right outside it
|
||||
!ptr::eq(self.parent(), parent)
|
||||
&& matches!(
|
||||
parent, AstNodes::ExpressionStatement(stmt) if !stmt.is_arrow_function_body()
|
||||
)
|
||||
!ptr::eq(self.parent(), parent) && matches!(parent, AstNodes::ExpressionStatement(_))
|
||||
};
|
||||
|
||||
match self.name.as_str() {
|
||||
@@ -114,9 +111,10 @@ impl NeedsParentheses<'_> for AstNode<'_, IdentifierReference<'_>> {
|
||||
let mut child_span = self.span;
|
||||
for parent in self.ancestors() {
|
||||
let dominated = match parent {
|
||||
AstNodes::ExpressionStatement(s) => {
|
||||
return is_computed_member_object && !s.is_arrow_function_body();
|
||||
AstNodes::ExpressionStatement(_) => {
|
||||
return is_computed_member_object;
|
||||
}
|
||||
AstNodes::ArrowFunctionExpression(_) => return false,
|
||||
AstNodes::ForStatement(s) => {
|
||||
return is_computed_member_object
|
||||
&& matches!(&s.init, Some(init) if init.span() == child_span);
|
||||
@@ -256,11 +254,10 @@ impl NeedsParentheses<'_> for AstNode<'_, StringLiteral<'_>> {
|
||||
// https://github.com/prettier/prettier/blob/00146ea15c30e16ad6526893c735e35683192efc/src/language-js/parentheses/needs-parentheses.js#L594-L609
|
||||
if let AstNodes::ExpressionStatement(stmt) = self.parent() {
|
||||
// `() => "foo"`
|
||||
!stmt.is_arrow_function_body()
|
||||
&& matches!(
|
||||
stmt.parent(),
|
||||
AstNodes::Program(_) | AstNodes::FunctionBody(_) | AstNodes::BlockStatement(_)
|
||||
)
|
||||
matches!(
|
||||
stmt.parent(),
|
||||
AstNodes::Program(_) | AstNodes::FunctionBody(_) | AstNodes::BlockStatement(_)
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -457,18 +454,11 @@ impl NeedsParentheses<'_> for AstNode<'_, BinaryExpression<'_>> {
|
||||
///
|
||||
/// <https://github.com/prettier/prettier/issues/907#issuecomment-284304321>
|
||||
fn is_in_for_initializer(expr: &AstNode<'_, BinaryExpression<'_>>) -> bool {
|
||||
let mut ancestors = expr.ancestors();
|
||||
let ancestors = expr.ancestors();
|
||||
|
||||
while let Some(parent) = ancestors.next() {
|
||||
for parent in ancestors {
|
||||
match parent {
|
||||
AstNodes::ExpressionStatement(stmt) => {
|
||||
if stmt.is_arrow_function_body() {
|
||||
// Expression body: `() => expr`
|
||||
// Skip `FunctionBody` and `ArrowFunctionExpression`
|
||||
let skipped = ancestors.by_ref().nth(1);
|
||||
debug_assert!(matches!(skipped, Some(AstNodes::ArrowFunctionExpression(_))));
|
||||
continue;
|
||||
}
|
||||
// Block body: `() => { expr; }` or `function() { expr; }` - continue checking
|
||||
// because for regular ForStatement, parens are still needed
|
||||
if matches!(stmt.parent(), AstNodes::FunctionBody(_)) {
|
||||
@@ -618,13 +608,7 @@ impl NeedsParentheses<'_> for AstNode<'_, AssignmentExpression<'_>> {
|
||||
// Expression statements, only object destructuring needs parens:
|
||||
// - `a = b` = no parens
|
||||
// - `{ x } = obj` -> `({ x } = obj)` = needed to prevent parsing as block statement
|
||||
// - `() => { x } = obj` -> `() => ({ x } = obj)` = needed in arrow function body
|
||||
// - `() => a = b` -> `() => (a = b)` = also parens needed
|
||||
AstNodes::ExpressionStatement(stmt) => {
|
||||
if stmt.is_arrow_function_body() {
|
||||
return true;
|
||||
}
|
||||
|
||||
AstNodes::ExpressionStatement(_) => {
|
||||
matches!(self.left, AssignmentTarget::ObjectAssignmentTarget(_))
|
||||
&& is_first_in_statement(
|
||||
self.span,
|
||||
@@ -632,6 +616,13 @@ impl NeedsParentheses<'_> for AstNode<'_, AssignmentExpression<'_>> {
|
||||
FirstInStatementMode::ExpressionStatementOrArrow,
|
||||
)
|
||||
}
|
||||
// Concise arrow bodies always need parens,
|
||||
// otherwise the `{` of an object pattern would parse as a block body
|
||||
// and a bare `=` reads as the arrow's own body:
|
||||
// - `() => { x } = obj` -> `() => ({ x } = obj)`
|
||||
// - `() => a = b` -> `() => (a = b)`
|
||||
#[expect(clippy::match_same_arms)]
|
||||
AstNodes::ArrowFunctionExpression(_) => true,
|
||||
// Sequence expressions, need to traverse up to find if we're in a for statement context:
|
||||
// - `a = 1, b = 2` in for loops don't need parens
|
||||
// - `(a = 1, b = 2)` elsewhere usually need parens
|
||||
@@ -696,9 +687,9 @@ impl NeedsParentheses<'_> for AstNode<'_, SequenceExpression<'_>> {
|
||||
match self.parent() {
|
||||
AstNodes::ReturnStatement(_)
|
||||
| AstNodes::ThrowStatement(_)
|
||||
| AstNodes::ArrowFunctionExpression(_)
|
||||
// There's a precedence for writing `x++, y++`
|
||||
| AstNodes::ForStatement(_) => false,
|
||||
AstNodes::ExpressionStatement(stmt) => !stmt.is_arrow_function_body(),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
@@ -1125,26 +1116,25 @@ fn is_first_in_statement(
|
||||
let is_not_first_iteration = index > 0;
|
||||
|
||||
match ancestor {
|
||||
AstNodes::ExpressionStatement(stmt) => {
|
||||
if stmt.is_arrow_function_body() {
|
||||
if mode == FirstInStatementMode::ExpressionStatementOrArrow {
|
||||
if is_not_first_iteration
|
||||
&& matches!(
|
||||
stmt.expression,
|
||||
AstNodes::ExpressionStatement(_) => return true,
|
||||
AstNodes::ArrowFunctionExpression(arrow) => {
|
||||
if mode == FirstInStatementMode::ExpressionStatementOrArrow {
|
||||
if is_not_first_iteration
|
||||
&& matches!(
|
||||
arrow.get_expression(),
|
||||
Some(
|
||||
Expression::SequenceExpression(_)
|
||||
| Expression::AssignmentExpression(_)
|
||||
)
|
||||
{
|
||||
// The original node doesn't need parens,
|
||||
// because an ancestor requires parens.
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
)
|
||||
{
|
||||
// The original node doesn't need parens,
|
||||
// because an ancestor requires parens.
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
AstNodes::StaticMemberExpression(_)
|
||||
| AstNodes::TaggedTemplateExpression(_)
|
||||
|
||||
@@ -118,11 +118,8 @@ impl<'a, 'b> FormatJsArrowFunctionExpression<'a, 'b> {
|
||||
);
|
||||
});
|
||||
|
||||
let format_body = FormatMaybeCachedFunctionBody {
|
||||
body,
|
||||
expression: arrow.expression(),
|
||||
mode: self.options.cache_mode,
|
||||
};
|
||||
let format_body =
|
||||
FormatContentWithCacheMode::new(body.span(), body, self.options.cache_mode);
|
||||
|
||||
// With arrays, arrow self and objects, they have a natural line breaking strategy:
|
||||
// Arrays and objects become blocks:
|
||||
@@ -181,7 +178,7 @@ impl<'a, 'b> FormatJsArrowFunctionExpression<'a, 'b> {
|
||||
if body_has_soft_line_break {
|
||||
write!(f, [space(), format_body]);
|
||||
} else {
|
||||
let should_add_parens = arrow.expression && should_add_parens(body);
|
||||
let should_add_parens = body.as_expression().is_some_and(should_add_parens);
|
||||
|
||||
let is_last_call_arg = matches!(
|
||||
self.options.call_argument_layout,
|
||||
@@ -273,11 +270,7 @@ impl<'a, 'b> ArrowFunctionLayout<'a, 'b> {
|
||||
|
||||
loop {
|
||||
if is_non_grouped_or_grouped_last_argument
|
||||
&& current.expression()
|
||||
&& let Some(AstNodes::ExpressionStatement(expr_stmt)) =
|
||||
current.body().statements().first().map(AstNode::<Statement>::as_ast_nodes)
|
||||
&& let AstNodes::ArrowFunctionExpression(next) =
|
||||
&expr_stmt.expression().as_ast_nodes()
|
||||
&& let AstNodes::ArrowFunctionExpression(next) = current.body().as_ast_nodes()
|
||||
{
|
||||
should_break = should_break || Self::should_break_chain(current);
|
||||
|
||||
@@ -636,11 +629,11 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ArrowChain<'a, '_> {
|
||||
});
|
||||
|
||||
let format_tail_body_inner = format_with(|f| {
|
||||
let format_tail_body = FormatMaybeCachedFunctionBody {
|
||||
body: tail_body,
|
||||
expression: tail.expression(),
|
||||
mode: self.options.cache_mode,
|
||||
};
|
||||
let format_tail_body = FormatContentWithCacheMode::new(
|
||||
tail_body.span(),
|
||||
tail_body,
|
||||
self.options.cache_mode,
|
||||
);
|
||||
|
||||
// Ensure that the parens of sequence expressions end up on their own line if the
|
||||
// body breaks
|
||||
@@ -653,7 +646,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ArrowChain<'a, '_> {
|
||||
write!(f, [token("("), format_tail_body, token(")")]);
|
||||
}
|
||||
} else {
|
||||
let should_add_parens = tail.expression && should_add_parens(tail_body);
|
||||
let should_add_parens = tail_body.as_expression().is_some_and(should_add_parens);
|
||||
if should_add_parens {
|
||||
// Known divergence from Prettier: with a signature that exactly fills the line,
|
||||
// Prettier breaks it because the hug layout's literal space is counted
|
||||
@@ -761,18 +754,13 @@ fn has_own_line_comment_before_body<'a>(
|
||||
f.comments().has_own_line_comment_in_range(signature_end, arrow.body().span().start)
|
||||
}
|
||||
|
||||
fn should_add_parens(body: &AstNode<'_, FunctionBody<'_>>) -> bool {
|
||||
let AstNodes::ExpressionStatement(stmt) = body.statements().first().unwrap().as_ast_nodes()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
fn should_add_parens(expression: &AstNode<'_, Expression<'_>>) -> bool {
|
||||
// Add parentheses to avoid confusion between `a => b ? c : d` and `a <= b ? c : d`
|
||||
// but only if the body isn't an object/function or class expression because parentheses are always required in that
|
||||
// case and added by the object expression itself
|
||||
if matches!(&stmt.expression, Expression::ConditionalExpression(_)) {
|
||||
if matches!(&**expression, Expression::ConditionalExpression(_)) {
|
||||
!matches!(
|
||||
ExpressionLeftSide::leftmost(stmt.expression()).as_ref(),
|
||||
ExpressionLeftSide::leftmost(expression).as_ref(),
|
||||
Expression::ObjectExpression(_)
|
||||
| Expression::FunctionExpression(_)
|
||||
| Expression::ClassExpression(_)
|
||||
@@ -840,33 +828,6 @@ fn format_signature<'a, 'b>(
|
||||
})
|
||||
}
|
||||
|
||||
/// Formats a function body with additional caching depending on [`mode`](Self::mode).
|
||||
pub struct FormatMaybeCachedFunctionBody<'a, 'b> {
|
||||
/// The body to format.
|
||||
pub body: &'b AstNode<'a, FunctionBody<'a>>,
|
||||
|
||||
/// Is the function body an arrow expression? i.e. `() => expr` instead of `() => {}`
|
||||
pub expression: bool,
|
||||
|
||||
/// If the body should be cached or if the formatter should try to retrieve it from the cache.
|
||||
pub mode: FunctionCacheMode,
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for FormatMaybeCachedFunctionBody<'a, '_> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let content = format_with(|f| {
|
||||
if self.expression
|
||||
&& let AstNodes::ExpressionStatement(s) =
|
||||
&self.body.statements().first().unwrap().as_ast_nodes()
|
||||
{
|
||||
return s.expression().fmt(f);
|
||||
}
|
||||
self.body.fmt(f);
|
||||
});
|
||||
FormatContentWithCacheMode::new(self.body.span, content, self.mode).fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
/// Format a sequence expression in an arrow function body that has a leading comment.
|
||||
///
|
||||
/// When an arrow function body is a sequence expression (e.g., `() => (a, b, c)`) and has
|
||||
|
||||
@@ -146,11 +146,11 @@ impl<'a, 'b> BinaryLikeExpression<'a, 'b> {
|
||||
AstNodes::ReturnStatement(_)
|
||||
| AstNodes::ThrowStatement(_)
|
||||
| AstNodes::ForStatement(_)
|
||||
| AstNodes::TemplateLiteral(_) => true,
|
||||
| AstNodes::TemplateLiteral(_)
|
||||
| AstNodes::ArrowFunctionExpression(_) => true,
|
||||
AstNodes::JSXExpressionContainer(container) => {
|
||||
matches!(container.parent(), AstNodes::JSXAttribute(_))
|
||||
}
|
||||
AstNodes::ExpressionStatement(statement) => statement.is_arrow_function_body(),
|
||||
AstNodes::ConditionalExpression(conditional) => !matches!(
|
||||
conditional.parent(),
|
||||
AstNodes::ReturnStatement(_)
|
||||
|
||||
@@ -323,7 +323,7 @@ fn should_group_first_argument(
|
||||
// fit entirely on the line or break fully. Only a single arrow
|
||||
// with a block body can be grouped to collapse the braces.
|
||||
Expression::ArrowFunctionExpression(arrow) => {
|
||||
if arrow.expression {
|
||||
if arrow.is_expression() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1010,6 +1010,11 @@ fn is_commonjs_or_amd_call(
|
||||
}
|
||||
}
|
||||
"define" => {
|
||||
// The AMD layout only applies in statement position, matching Prettier's
|
||||
// `parent.type === "ExpressionStatement"` check. A concise arrow body is not statement
|
||||
// position: its parent is the `ArrowFunctionExpression`, so `() => define(...)` formats
|
||||
// as a normal call. (oxc used to accept it, because concise bodies were wrapped in a
|
||||
// synthetic `ExpressionStatement` before `ArrowFunctionBody` existed.)
|
||||
let in_statement = matches!(call.parent(), AstNodes::ExpressionStatement(_));
|
||||
if in_statement {
|
||||
match arguments.len() {
|
||||
@@ -1102,7 +1107,7 @@ fn is_react_hook_with_deps_array(
|
||||
return false;
|
||||
}
|
||||
|
||||
if callback.expression {
|
||||
if callback.is_expression() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1134,7 +1139,7 @@ fn is_decorated_function(argument: &AstNode<'_, Argument<'_>>) -> bool {
|
||||
return false;
|
||||
};
|
||||
|
||||
if arrow.expression {
|
||||
if arrow.is_expression() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,7 @@ use crate::{
|
||||
ast_nodes::AstNode,
|
||||
format_args,
|
||||
formatter::{Buffer, prelude::*, trivia::FormatLeadingComments},
|
||||
print::{
|
||||
arrow_function_expression::FormatMaybeCachedFunctionBody, semicolon::OptionalSemicolon,
|
||||
},
|
||||
print::semicolon::OptionalSemicolon,
|
||||
write,
|
||||
};
|
||||
|
||||
@@ -127,11 +125,7 @@ impl<'a, 'b> FormatFunction<'a, 'b> {
|
||||
f,
|
||||
[
|
||||
space(),
|
||||
FormatMaybeCachedFunctionBody {
|
||||
body,
|
||||
mode: self.options.cache_mode,
|
||||
expression: false
|
||||
}
|
||||
FormatContentWithCacheMode::new(body.span, body, self.options.cache_mode)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,9 +40,8 @@ impl<'a> AnyJsxTagWithChildren<'a, '_> {
|
||||
}
|
||||
|
||||
fn format_trailing_comments(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let trailing_comments = if let AstNodes::ArrowFunctionExpression(arrow) =
|
||||
self.parent().parent().parent()
|
||||
&& arrow.expression
|
||||
let trailing_comments = if let AstNodes::ArrowFunctionExpression(arrow) = self.parent()
|
||||
&& arrow.is_expression()
|
||||
{
|
||||
f.context().comments().comments_before(arrow.span.end)
|
||||
} else if let AstNodes::ConditionalExpression(conditional) = self.parent() {
|
||||
@@ -94,7 +93,8 @@ impl<'a> AnyJsxTagWithChildren<'a, '_> {
|
||||
AstNodes::ArrayExpression(_)
|
||||
| AstNodes::JSXAttribute(_)
|
||||
| AstNodes::JSXExpressionContainer(_)
|
||||
| AstNodes::ConditionalExpression(_) => WrapState::NoWrap,
|
||||
| AstNodes::ConditionalExpression(_)
|
||||
| AstNodes::ExpressionStatement(_) => WrapState::NoWrap,
|
||||
AstNodes::StaticMemberExpression(member) => {
|
||||
if member.optional {
|
||||
WrapState::NoWrap
|
||||
@@ -107,15 +107,6 @@ impl<'a> AnyJsxTagWithChildren<'a, '_> {
|
||||
WrapState::NoWrap
|
||||
}
|
||||
AstNodes::NewExpression(new) if new.is_argument_span(self.span()) => WrapState::NoWrap,
|
||||
AstNodes::ExpressionStatement(stmt) => {
|
||||
// `() => <div></div>`
|
||||
// ^^^^^^^^^^^
|
||||
if stmt.is_arrow_function_body() {
|
||||
WrapState::WrapOnBreak
|
||||
} else {
|
||||
WrapState::NoWrap
|
||||
}
|
||||
}
|
||||
AstNodes::ComputedMemberExpression(member) => {
|
||||
if member.optional {
|
||||
WrapState::NoWrap
|
||||
@@ -123,6 +114,9 @@ impl<'a> AnyJsxTagWithChildren<'a, '_> {
|
||||
WrapState::WrapOnBreak
|
||||
}
|
||||
}
|
||||
// Concise arrow body: `() => <div></div>`
|
||||
#[expect(clippy::match_same_arms)]
|
||||
AstNodes::ArrowFunctionExpression(_) => WrapState::WrapOnBreak,
|
||||
_ => WrapState::WrapOnBreak,
|
||||
}
|
||||
}
|
||||
@@ -252,14 +246,9 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AnyJsxTagWithChildren<'a, '_> {
|
||||
/// // As JSX attribute:
|
||||
/// <Tooltip title={[].map(name => (<Foo>{name}</Foo>))} />;
|
||||
/// ```
|
||||
pub fn should_expand(mut parent: &AstNodes<'_>) -> bool {
|
||||
if let AstNodes::ExpressionStatement(stmt) = parent {
|
||||
// If the parent is a JSXExpressionContainer, we need to check its parent
|
||||
// to determine if it should expand.
|
||||
parent = stmt.grand_parent();
|
||||
}
|
||||
pub fn should_expand(parent: &AstNodes<'_>) -> bool {
|
||||
let maybe_jsx_expression_container = match parent {
|
||||
AstNodes::ArrowFunctionExpression(arrow) if arrow.expression => match arrow.parent() {
|
||||
AstNodes::ArrowFunctionExpression(arrow) if arrow.is_expression() => match arrow.parent() {
|
||||
AstNodes::CallExpression(call) => call.parent(),
|
||||
_ => return false,
|
||||
},
|
||||
|
||||
@@ -443,7 +443,8 @@ impl<'a> FormatWrite<'a> for AstNode<'a, AwaitExpression<'a>> {
|
||||
let mut await_expression = None;
|
||||
for ancestor in self.ancestors().skip(1) {
|
||||
match ancestor {
|
||||
AstNodes::BlockStatement(_)
|
||||
AstNodes::ArrowFunctionExpression(_)
|
||||
| AstNodes::BlockStatement(_)
|
||||
| AstNodes::FunctionBody(_)
|
||||
| AstNodes::SwitchCase(_)
|
||||
| AstNodes::Program(_)
|
||||
|
||||
@@ -71,7 +71,7 @@ pub fn keeps_trailing_comment_inside_parens(expr: &Expression<'_>, gated: bool)
|
||||
right => keeps_trailing_comment_inside_parens(right, false),
|
||||
}
|
||||
}
|
||||
Expression::ArrowFunctionExpression(arrow) if arrow.expression => arrow
|
||||
Expression::ArrowFunctionExpression(arrow) => arrow
|
||||
.get_expression()
|
||||
.is_some_and(|body| keeps_trailing_comment_inside_parens(body, true)),
|
||||
_ => false,
|
||||
@@ -91,9 +91,10 @@ pub fn write_trailing_comments_inside_parens<'a>(
|
||||
is_sequence: bool,
|
||||
) {
|
||||
let parens_survive = match parent {
|
||||
AstNodes::VariableDeclarator(_) | AstNodes::ReturnStatement(_) => true,
|
||||
AstNodes::ArrowFunctionExpression(_)
|
||||
| AstNodes::VariableDeclarator(_)
|
||||
| AstNodes::ReturnStatement(_) => true,
|
||||
AstNodes::AssignmentExpression(_) => is_sequence,
|
||||
AstNodes::ExpressionStatement(statement) => statement.is_arrow_function_body(),
|
||||
_ => false,
|
||||
};
|
||||
if parens_survive
|
||||
|
||||
@@ -11,10 +11,7 @@ use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, SequenceExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let is_arrow_body = matches!(
|
||||
self.parent(),
|
||||
AstNodes::ExpressionStatement(statement) if statement.is_arrow_function_body()
|
||||
);
|
||||
let is_arrow_body = matches!(self.parent(), AstNodes::ArrowFunctionExpression(_));
|
||||
|
||||
let format_inner = format_with(|f| {
|
||||
let mut expressions = self.expressions().iter();
|
||||
@@ -36,8 +33,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, SequenceExpression<'a>> {
|
||||
});
|
||||
|
||||
if matches!(self.parent(), AstNodes::ForStatement(_))
|
||||
|| (matches!(self.parent(), AstNodes::ExpressionStatement(statement)
|
||||
if !statement.is_arrow_function_body()))
|
||||
|| matches!(self.parent(), AstNodes::ExpressionStatement(_))
|
||||
{
|
||||
write!(f, [indent(&rest)]);
|
||||
} else {
|
||||
|
||||
@@ -552,9 +552,13 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
// Determine if the chain is eligible based on the following checks:
|
||||
// 1. For variable declarators: only continue if this isn't the final assignment in the chain
|
||||
(matches!(parent, AstNodes::VariableDeclarator(_)) && !right_is_tail) ||
|
||||
// 2. For assignment expressions: continue unless this is the final assignment in an expression statement
|
||||
// 2. For assignment expressions: continue unless this is the final assignment
|
||||
// in an expression statement or concise arrow body.
|
||||
matches!(parent, AstNodes::AssignmentExpression(parent_assignment)
|
||||
if !right_is_tail || !matches!(parent_assignment.parent(), AstNodes::ExpressionStatement(_))
|
||||
if !right_is_tail || !matches!(
|
||||
parent_assignment.parent(),
|
||||
AstNodes::ArrowFunctionExpression(_) | AstNodes::ExpressionStatement(_)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
false
|
||||
@@ -564,14 +568,11 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
if right_is_tail {
|
||||
match right_expression {
|
||||
Expression::ArrowFunctionExpression(arrow) => {
|
||||
if arrow.expression {
|
||||
let Statement::ExpressionStatement(stmt) = &arrow.body.statements[0]
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
if matches!(&stmt.expression, Expression::ArrowFunctionExpression(_)) {
|
||||
return Some(AssignmentLikeLayout::ChainTailArrowFunction);
|
||||
}
|
||||
if matches!(
|
||||
arrow.get_expression(),
|
||||
Some(Expression::ArrowFunctionExpression(_))
|
||||
) {
|
||||
return Some(AssignmentLikeLayout::ChainTailArrowFunction);
|
||||
}
|
||||
Some(AssignmentLikeLayout::ChainTail)
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ pub fn is_test_call_expression(call: &AstNode<CallExpression<'_>>) -> bool {
|
||||
(function.params.parameters_count(), true)
|
||||
}
|
||||
Argument::ArrowFunctionExpression(arrow) => {
|
||||
(arrow.params.parameters_count(), !arrow.expression)
|
||||
(arrow.params.parameters_count(), !arrow.is_expression())
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
@@ -93,7 +93,7 @@ impl<'a, 'b> MemberChain<'a, 'b> {
|
||||
has_computed_property ||
|
||||
is_factory(&identifier.name) ||
|
||||
// If an identifier has a name that is shorter than the tab width, then we join it with the "head"
|
||||
(matches!(parent.without_chain_expression(), AstNodes::ExpressionStatement(stmt) if !stmt.is_arrow_function_body())
|
||||
(matches!(parent.without_chain_expression(), AstNodes::ExpressionStatement(_))
|
||||
&& has_short_name(&identifier.name, f.options().indent_width.value()))
|
||||
}
|
||||
Expression::ThisExpression(_) => true,
|
||||
|
||||
+7
-6
@@ -31,12 +31,13 @@ const assignmentChain = () =>
|
||||
thirdVeryLongIdentifier);
|
||||
|
||||
const amd = () =>
|
||||
define("a-very-long-module-name", [
|
||||
"first-long-dependency",
|
||||
"second-long-dependency",
|
||||
], () => {
|
||||
return value;
|
||||
});
|
||||
define(
|
||||
"a-very-long-module-name",
|
||||
["first-long-dependency", "second-long-dependency"],
|
||||
() => {
|
||||
return value;
|
||||
},
|
||||
);
|
||||
|
||||
-------------------
|
||||
{ printWidth: 100 }
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// The AMD `define` layout (arguments hugged on one line, never broken) applies only in
|
||||
// statement position, matching Prettier. A concise arrow body is not statement position.
|
||||
|
||||
// Statement position: AMD layout.
|
||||
define(["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
|
||||
define("name", ["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
|
||||
// Concise arrow body: NOT statement position, so the arguments break normally.
|
||||
const d = () => define(["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
|
||||
// Block body: the call is a statement again, so AMD layout applies.
|
||||
const e = () => {
|
||||
define(["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
};
|
||||
|
||||
// `require` is not gated on statement position, so a concise body keeps its layout.
|
||||
const r = () => require("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
---
|
||||
source: crates/oxc_formatter/tests/fixtures/mod.rs
|
||||
---
|
||||
==================== Input ====================
|
||||
// The AMD `define` layout (arguments hugged on one line, never broken) applies only in
|
||||
// statement position, matching Prettier. A concise arrow body is not statement position.
|
||||
|
||||
// Statement position: AMD layout.
|
||||
define(["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
|
||||
define("name", ["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
|
||||
// Concise arrow body: NOT statement position, so the arguments break normally.
|
||||
const d = () => define(["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
|
||||
// Block body: the call is a statement again, so AMD layout applies.
|
||||
const e = () => {
|
||||
define(["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
};
|
||||
|
||||
// `require` is not gated on statement position, so a concise body keeps its layout.
|
||||
const r = () => require("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
|
||||
==================== Output ====================
|
||||
------------------
|
||||
{ printWidth: 80 }
|
||||
------------------
|
||||
// The AMD `define` layout (arguments hugged on one line, never broken) applies only in
|
||||
// statement position, matching Prettier. A concise arrow body is not statement position.
|
||||
|
||||
// Statement position: AMD layout.
|
||||
define([
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
], () => {});
|
||||
|
||||
define("name", [
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
], () => {});
|
||||
|
||||
// Concise arrow body: NOT statement position, so the arguments break normally.
|
||||
const d = () =>
|
||||
define(
|
||||
["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"],
|
||||
() => {},
|
||||
);
|
||||
|
||||
// Block body: the call is a statement again, so AMD layout applies.
|
||||
const e = () => {
|
||||
define([
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
], () => {});
|
||||
};
|
||||
|
||||
// `require` is not gated on statement position, so a concise body keeps its layout.
|
||||
const r = () =>
|
||||
require("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
|
||||
-------------------
|
||||
{ printWidth: 100 }
|
||||
-------------------
|
||||
// The AMD `define` layout (arguments hugged on one line, never broken) applies only in
|
||||
// statement position, matching Prettier. A concise arrow body is not statement position.
|
||||
|
||||
// Statement position: AMD layout.
|
||||
define(["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
|
||||
define("name", ["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
|
||||
// Concise arrow body: NOT statement position, so the arguments break normally.
|
||||
const d = () => define(["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
|
||||
// Block body: the call is a statement again, so AMD layout applies.
|
||||
const e = () => {
|
||||
define(["aaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"], () => {});
|
||||
};
|
||||
|
||||
// `require` is not gated on statement position, so a concise body keeps its layout.
|
||||
const r = () => require("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
|
||||
===================== End =====================
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// Own-line comment *inside* the source parentheses of a concise arrow body.
|
||||
// The parens are not part of the AST, so the body's span starts at the expression:
|
||||
// the comment is a leading comment of the body, and the body moves to its own line.
|
||||
// (Distinct from `arrow-body-leading-comment-in-call.js`, where the comment sits
|
||||
// *before* the opening paren.)
|
||||
|
||||
const array = () => (
|
||||
// c
|
||||
[1, 2, 3]
|
||||
);
|
||||
|
||||
const object = () => (
|
||||
// c
|
||||
({ a: 1 })
|
||||
);
|
||||
|
||||
const arrow = () => (
|
||||
// c
|
||||
() => 1
|
||||
);
|
||||
|
||||
const template = () => (
|
||||
// c
|
||||
`tpl ${x}
|
||||
more`
|
||||
);
|
||||
|
||||
// Block comment on the same line as the expression stays hugged.
|
||||
const inlineBlock = () => (
|
||||
/* c */ [1, 2, 3]
|
||||
);
|
||||
|
||||
// Chain tail body.
|
||||
const chainTail = (a) => (b) => (
|
||||
// c
|
||||
[1, 2]
|
||||
);
|
||||
|
||||
// As a call argument: the body breaks and the call gets a trailing comma.
|
||||
foo(() => (
|
||||
// c
|
||||
[1, 2, 3]
|
||||
));
|
||||
|
||||
foo((a) => (b) => (
|
||||
// c
|
||||
[1, 2]
|
||||
));
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
---
|
||||
source: crates/oxc_formatter/tests/fixtures/mod.rs
|
||||
---
|
||||
==================== Input ====================
|
||||
// Own-line comment *inside* the source parentheses of a concise arrow body.
|
||||
// The parens are not part of the AST, so the body's span starts at the expression:
|
||||
// the comment is a leading comment of the body, and the body moves to its own line.
|
||||
// (Distinct from `arrow-body-leading-comment-in-call.js`, where the comment sits
|
||||
// *before* the opening paren.)
|
||||
|
||||
const array = () => (
|
||||
// c
|
||||
[1, 2, 3]
|
||||
);
|
||||
|
||||
const object = () => (
|
||||
// c
|
||||
({ a: 1 })
|
||||
);
|
||||
|
||||
const arrow = () => (
|
||||
// c
|
||||
() => 1
|
||||
);
|
||||
|
||||
const template = () => (
|
||||
// c
|
||||
`tpl ${x}
|
||||
more`
|
||||
);
|
||||
|
||||
// Block comment on the same line as the expression stays hugged.
|
||||
const inlineBlock = () => (
|
||||
/* c */ [1, 2, 3]
|
||||
);
|
||||
|
||||
// Chain tail body.
|
||||
const chainTail = (a) => (b) => (
|
||||
// c
|
||||
[1, 2]
|
||||
);
|
||||
|
||||
// As a call argument: the body breaks and the call gets a trailing comma.
|
||||
foo(() => (
|
||||
// c
|
||||
[1, 2, 3]
|
||||
));
|
||||
|
||||
foo((a) => (b) => (
|
||||
// c
|
||||
[1, 2]
|
||||
));
|
||||
|
||||
==================== Output ====================
|
||||
------------------
|
||||
{ printWidth: 80 }
|
||||
------------------
|
||||
// Own-line comment *inside* the source parentheses of a concise arrow body.
|
||||
// The parens are not part of the AST, so the body's span starts at the expression:
|
||||
// the comment is a leading comment of the body, and the body moves to its own line.
|
||||
// (Distinct from `arrow-body-leading-comment-in-call.js`, where the comment sits
|
||||
// *before* the opening paren.)
|
||||
|
||||
const array = () =>
|
||||
// c
|
||||
[1, 2, 3];
|
||||
|
||||
const object = () =>
|
||||
// c
|
||||
({ a: 1 });
|
||||
|
||||
const arrow =
|
||||
() =>
|
||||
// c
|
||||
() =>
|
||||
1;
|
||||
|
||||
const template = () =>
|
||||
// c
|
||||
`tpl ${x}
|
||||
more`;
|
||||
|
||||
// Block comment on the same line as the expression stays hugged.
|
||||
const inlineBlock = () => /* c */ [1, 2, 3];
|
||||
|
||||
// Chain tail body.
|
||||
const chainTail = (a) => (b) =>
|
||||
// c
|
||||
[1, 2];
|
||||
|
||||
// As a call argument: the body breaks and the call gets a trailing comma.
|
||||
foo(() =>
|
||||
// c
|
||||
[1, 2, 3],
|
||||
);
|
||||
|
||||
foo((a) => (b) =>
|
||||
// c
|
||||
[1, 2],
|
||||
);
|
||||
|
||||
-------------------
|
||||
{ printWidth: 100 }
|
||||
-------------------
|
||||
// Own-line comment *inside* the source parentheses of a concise arrow body.
|
||||
// The parens are not part of the AST, so the body's span starts at the expression:
|
||||
// the comment is a leading comment of the body, and the body moves to its own line.
|
||||
// (Distinct from `arrow-body-leading-comment-in-call.js`, where the comment sits
|
||||
// *before* the opening paren.)
|
||||
|
||||
const array = () =>
|
||||
// c
|
||||
[1, 2, 3];
|
||||
|
||||
const object = () =>
|
||||
// c
|
||||
({ a: 1 });
|
||||
|
||||
const arrow =
|
||||
() =>
|
||||
// c
|
||||
() =>
|
||||
1;
|
||||
|
||||
const template = () =>
|
||||
// c
|
||||
`tpl ${x}
|
||||
more`;
|
||||
|
||||
// Block comment on the same line as the expression stays hugged.
|
||||
const inlineBlock = () => /* c */ [1, 2, 3];
|
||||
|
||||
// Chain tail body.
|
||||
const chainTail = (a) => (b) =>
|
||||
// c
|
||||
[1, 2];
|
||||
|
||||
// As a call argument: the body breaks and the call gets a trailing comma.
|
||||
foo(() =>
|
||||
// c
|
||||
[1, 2, 3],
|
||||
);
|
||||
|
||||
foo((a) => (b) =>
|
||||
// c
|
||||
[1, 2],
|
||||
);
|
||||
|
||||
===================== End =====================
|
||||
@@ -1,7 +1,10 @@
|
||||
use oxc_allocator::{ArenaBox, CloneIn, GetAllocator};
|
||||
use oxc_ast::ast::{
|
||||
ArrowFunctionExpression, Expression, FormalParameter, Function, Statement, TSType,
|
||||
TSTypeAnnotation, UnaryExpression,
|
||||
use oxc_ast::{
|
||||
ast::{
|
||||
ArrowFunctionBody, ArrowFunctionExpression, Expression, FormalParameter, Function, TSType,
|
||||
TSTypeAnnotation, UnaryExpression,
|
||||
},
|
||||
match_expression,
|
||||
};
|
||||
use oxc_span::SPAN;
|
||||
|
||||
@@ -116,16 +119,15 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
return None;
|
||||
}
|
||||
|
||||
if function.expression
|
||||
&& let Some(Statement::ExpressionStatement(stmt)) = function.body.statements.first()
|
||||
{
|
||||
return self
|
||||
.infer_type_from_expression(&stmt.expression)
|
||||
.map(|type_annotation| TSTypeAnnotation::boxed(SPAN, type_annotation, self));
|
||||
match &function.body {
|
||||
ArrowFunctionBody::FunctionBody(function_body) => {
|
||||
FunctionReturnType::infer(self, function_body)
|
||||
.map(|type_annotation| TSTypeAnnotation::boxed(SPAN, type_annotation, self))
|
||||
}
|
||||
expression @ match_expression!(ArrowFunctionBody) => self
|
||||
.infer_type_from_expression(expression.to_expression())
|
||||
.map(|type_annotation| TSTypeAnnotation::boxed(SPAN, type_annotation, self)),
|
||||
}
|
||||
|
||||
FunctionReturnType::infer(self, &function.body)
|
||||
.map(|type_annotation| TSTypeAnnotation::boxed(SPAN, type_annotation, self))
|
||||
}
|
||||
|
||||
pub(crate) fn is_need_to_infer_type_from_expression(expr: &Expression<'a>) -> bool {
|
||||
|
||||
@@ -48,7 +48,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
if return_type.is_none() {
|
||||
self.error(function_must_have_explicit_return_type(Span::new(
|
||||
func.params.span.start,
|
||||
func.body.span.start + 1,
|
||||
func.body.span().start + 1,
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
@@ -784,29 +784,11 @@ pub fn is_default_this_binding<'a>(
|
||||
_ => return true,
|
||||
}
|
||||
}
|
||||
AstKind::ExpressionStatement(expr_stmt) => {
|
||||
let Some(function_body) = outermost_paren_parent(parent, semantic) else {
|
||||
return true;
|
||||
};
|
||||
let AstKind::FunctionBody(_) = function_body.kind() else {
|
||||
return true;
|
||||
};
|
||||
let Some(arrow_func) = outermost_paren_parent(function_body, semantic) else {
|
||||
return true;
|
||||
};
|
||||
let AstKind::ArrowFunctionExpression(expr) = arrow_func.kind() else {
|
||||
return true;
|
||||
};
|
||||
if !expr.expression
|
||||
|| expr_stmt.expression.span() != current_node.span()
|
||||
|| !is_callee(arrow_func, semantic)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
current_node = outermost_paren_parent(arrow_func, semantic).unwrap();
|
||||
}
|
||||
AstKind::ArrowFunctionExpression(expr) => {
|
||||
if current_node.span() != expr.body.span || !is_callee(parent, semantic) {
|
||||
if !expr.is_expression()
|
||||
|| current_node.span() != expr.body.span()
|
||||
|| !is_callee(parent, semantic)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
current_node = outermost_paren_parent(parent, semantic).unwrap();
|
||||
|
||||
+4
-2
@@ -1410,8 +1410,10 @@ impl RuleRunner for crate::rules::eslint::radix::Radix {
|
||||
}
|
||||
|
||||
impl RuleRunner for crate::rules::eslint::require_await::RequireAwait {
|
||||
const NODE_TYPES: Option<&AstTypesBitset> =
|
||||
Some(&AstTypesBitset::from_types(&[AstType::FunctionBody]));
|
||||
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
|
||||
AstType::ArrowFunctionExpression,
|
||||
AstType::FunctionBody,
|
||||
]));
|
||||
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
pub mod return_checker;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use oxc_ast::{
|
||||
AstKind,
|
||||
ast::{Expression, FunctionBody, Statement},
|
||||
@@ -10,10 +11,7 @@ use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use self::return_checker::{StatementReturnStatus, check_function_body, is_void_arrow_return};
|
||||
use crate::{
|
||||
AstNode,
|
||||
ast_util::{get_enclosing_function, outermost_paren},
|
||||
@@ -21,6 +19,10 @@ use crate::{
|
||||
rule::{DefaultRuleConfig, Rule},
|
||||
};
|
||||
|
||||
pub mod return_checker;
|
||||
|
||||
use self::return_checker::{StatementReturnStatus, check_function_body};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum MissingReturnHint {
|
||||
SwitchWithoutDefault,
|
||||
@@ -173,18 +175,25 @@ impl Rule for ArrayCallbackReturn {
|
||||
}
|
||||
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
let (function_body, always_explicit_return, is_async_empty) = match node.kind() {
|
||||
let (function_body, concise_expression, always_explicit_return, is_async_empty) = match node
|
||||
.kind()
|
||||
{
|
||||
// Async, generator, and single expression arrow functions
|
||||
// always have explicit return value
|
||||
AstKind::ArrowFunctionExpression(arrow) => (
|
||||
&arrow.body,
|
||||
arrow.r#async || arrow.expression,
|
||||
arrow.r#async && arrow.body.statements.is_empty(),
|
||||
),
|
||||
AstKind::ArrowFunctionExpression(arrow) => {
|
||||
let function_body = arrow.get_function_body();
|
||||
(
|
||||
function_body,
|
||||
arrow.get_expression(),
|
||||
arrow.r#async || arrow.is_expression(),
|
||||
arrow.r#async && function_body.is_some_and(|body| body.statements.is_empty()),
|
||||
)
|
||||
}
|
||||
AstKind::Function(function) => {
|
||||
if let Some(body) = &function.body {
|
||||
(
|
||||
body,
|
||||
Some(body.as_ref()),
|
||||
None,
|
||||
function.r#async || function.generator,
|
||||
function.r#async && body.statements.is_empty(),
|
||||
)
|
||||
@@ -207,10 +216,16 @@ impl Rule for ArrayCallbackReturn {
|
||||
("forEach", false, _, _) => (),
|
||||
("forEach", true, false, _) => {
|
||||
if return_status.may_return_explicit() {
|
||||
let return_spans = return_checker::get_explicit_return_spans(function_body)
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| function_body.span());
|
||||
let return_spans = concise_expression.map_or_else(
|
||||
|| {
|
||||
let function_body = function_body.unwrap();
|
||||
return_checker::get_explicit_return_spans(function_body)
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| function_body.span())
|
||||
},
|
||||
GetSpan::span,
|
||||
);
|
||||
|
||||
ctx.diagnostic(expect_no_return(
|
||||
&full_array_method_name(array_method),
|
||||
@@ -224,10 +239,21 @@ impl Rule for ArrayCallbackReturn {
|
||||
return;
|
||||
}
|
||||
|
||||
if is_void_arrow_return(&function_body.statements) {
|
||||
if concise_expression.is_some_and(Expression::is_void) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(expression) = concise_expression {
|
||||
ctx.diagnostic(expect_void_return(
|
||||
&full_array_method_name(array_method),
|
||||
array_method_span,
|
||||
expression.span(),
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
let function_body = function_body.unwrap();
|
||||
|
||||
let (return_spans, has_void) =
|
||||
return_checker::get_no_voided_return_spans(function_body, self.allow_void);
|
||||
|
||||
@@ -251,7 +277,7 @@ impl Rule for ArrayCallbackReturn {
|
||||
ctx.diagnostic(expect_return(
|
||||
&full_array_method_name(array_method),
|
||||
array_method_span,
|
||||
function_body,
|
||||
function_body.unwrap(),
|
||||
self.allow_implicit,
|
||||
));
|
||||
}
|
||||
@@ -261,7 +287,7 @@ impl Rule for ArrayCallbackReturn {
|
||||
ctx.diagnostic(expect_return(
|
||||
&full_array_method_name(array_method),
|
||||
array_method_span,
|
||||
function_body,
|
||||
function_body.unwrap(),
|
||||
self.allow_implicit,
|
||||
));
|
||||
}
|
||||
@@ -271,7 +297,7 @@ impl Rule for ArrayCallbackReturn {
|
||||
ctx.diagnostic(expect_return(
|
||||
&full_array_method_name(array_method),
|
||||
array_method_span,
|
||||
function_body,
|
||||
function_body.unwrap(),
|
||||
self.allow_implicit,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use oxc_allocator::ArenaVec;
|
||||
use oxc_ast::ast::{ArrowFunctionExpression, Function, FunctionBody, ReturnStatement, Statement};
|
||||
use oxc_ast::ast::{ArrowFunctionExpression, Function, FunctionBody, ReturnStatement};
|
||||
use oxc_ast_visit::VisitJs;
|
||||
use oxc_cfg::{
|
||||
EdgeType, InstructionKind, ReturnInstructionKind,
|
||||
@@ -230,23 +229,3 @@ impl VisitJs<'_> for ReturnStatementFinder {
|
||||
|
||||
fn visit_arrow_function_expression(&mut self, _it: &ArrowFunctionExpression<'_>) {}
|
||||
}
|
||||
|
||||
pub fn is_void_arrow_return(statements: &ArenaVec<'_, Statement>) -> bool {
|
||||
if statements.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if statements.len() > 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Some(statement_return) = statements.first() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Statement::ExpressionStatement(expression_return) = statement_return else {
|
||||
return false;
|
||||
};
|
||||
|
||||
expression_return.expression.is_void()
|
||||
}
|
||||
@@ -193,7 +193,7 @@ impl Rule for ArrowBodyStyle {
|
||||
return;
|
||||
};
|
||||
|
||||
if arrow_func_expr.expression {
|
||||
if arrow_func_expr.is_expression() {
|
||||
self.run_for_arrow_expression(arrow_func_expr, ctx);
|
||||
} else {
|
||||
self.run_for_arrow_block(arrow_func_expr, node, ctx);
|
||||
@@ -222,7 +222,7 @@ impl ArrowBodyStyle {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.diagnostic_with_fix(expected_block_diagnostic(arrow_func_expr.body.span), |fixer| {
|
||||
ctx.diagnostic_with_fix(expected_block_diagnostic(arrow_func_expr.body.span()), |fixer| {
|
||||
Self::fix_concise_to_block(arrow_func_expr, fixer, ctx)
|
||||
});
|
||||
}
|
||||
@@ -238,7 +238,7 @@ impl ArrowBodyStyle {
|
||||
ctx: &LintContext<'a>,
|
||||
) {
|
||||
let ArrowBodyStyle(mode, _config) = &self;
|
||||
let body = &arrow_func_expr.body;
|
||||
let body = arrow_func_expr.get_function_body().unwrap();
|
||||
|
||||
match mode {
|
||||
Mode::Never => {
|
||||
@@ -328,11 +328,11 @@ impl ArrowBodyStyle {
|
||||
let inner_expr = expr.get_inner_expression();
|
||||
if matches!(inner_expr, Expression::ObjectExpression(_)) {
|
||||
let inner_text = ctx.source_range(inner_expr.span());
|
||||
return fixer.replace(body.span, format!("{{return {inner_text}}}"));
|
||||
return fixer.replace(body.span(), format!("{{return {inner_text}}}"));
|
||||
}
|
||||
|
||||
// For all other expressions, just wrap in `{ return ... }`
|
||||
fixer.replace(body.span, format!("{{return {expr_text}}}"))
|
||||
fixer.replace(body.span(), format!("{{return {expr_text}}}"))
|
||||
}
|
||||
|
||||
/// Fix: Convert block body to concise body
|
||||
@@ -376,11 +376,11 @@ impl ArrowBodyStyle {
|
||||
fix.push(fixer.delete_range(Span::sized(return_statement.span.end - 1, 1)));
|
||||
}
|
||||
fix.push(fixer.replace(
|
||||
Span::sized(arrow_func_expr.body.span.start, 1),
|
||||
Span::sized(arrow_func_expr.body.span().start, 1),
|
||||
if needs_parens { "(" } else { "" },
|
||||
));
|
||||
fix.push(fixer.replace(
|
||||
Span::sized(arrow_func_expr.body.span.end - 1, 1),
|
||||
Span::sized(arrow_func_expr.body.span().end - 1, 1),
|
||||
if needs_parens { ")" } else { "" },
|
||||
));
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use oxc_ast::{
|
||||
AstKind,
|
||||
ast::{AccessorProperty, Expression, PropertyDefinition, TSAccessibility},
|
||||
ast::{
|
||||
AccessorProperty, ArrowFunctionBody, Expression, FunctionBody, PropertyDefinition,
|
||||
TSAccessibility,
|
||||
},
|
||||
};
|
||||
use oxc_ast_visit::Visit;
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
@@ -174,6 +177,11 @@ impl Rule for ClassMethodsUseThis {
|
||||
}
|
||||
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
enum FunctionLikeBody<'a, 'b> {
|
||||
Function(&'b FunctionBody<'a>),
|
||||
Arrow(&'b ArrowFunctionBody<'a>),
|
||||
}
|
||||
|
||||
let function_pair = match node.kind() {
|
||||
AstKind::AccessorProperty(accessor) => {
|
||||
if accessor.r#static
|
||||
@@ -190,11 +198,12 @@ impl Rule for ClassMethodsUseThis {
|
||||
}
|
||||
accessor.value.as_ref().and_then(|value| match value {
|
||||
Expression::ArrowFunctionExpression(arrow_function) => {
|
||||
Some((&arrow_function.body, &accessor.key))
|
||||
}
|
||||
Expression::FunctionExpression(function_expression) => {
|
||||
Some((function_expression.body.as_ref()?, &accessor.key))
|
||||
Some((FunctionLikeBody::Arrow(&arrow_function.body), &accessor.key))
|
||||
}
|
||||
Expression::FunctionExpression(function_expression) => Some((
|
||||
FunctionLikeBody::Function(function_expression.body.as_deref()?),
|
||||
&accessor.key,
|
||||
)),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
@@ -212,7 +221,7 @@ impl Rule for ClassMethodsUseThis {
|
||||
return;
|
||||
}
|
||||
let Some(function_body) = method_definition.value.body.as_ref() else { return };
|
||||
Some((function_body, &method_definition.key))
|
||||
Some((FunctionLikeBody::Function(function_body), &method_definition.key))
|
||||
}
|
||||
AstKind::PropertyDefinition(property_definition) => {
|
||||
if property_definition.r#static
|
||||
@@ -228,12 +237,14 @@ impl Rule for ClassMethodsUseThis {
|
||||
return;
|
||||
}
|
||||
property_definition.value.as_ref().and_then(|value| match value {
|
||||
Expression::ArrowFunctionExpression(arrow_function) => {
|
||||
Some((&arrow_function.body, &property_definition.key))
|
||||
}
|
||||
Expression::FunctionExpression(function_expression) => {
|
||||
Some((function_expression.body.as_ref()?, &property_definition.key))
|
||||
}
|
||||
Expression::ArrowFunctionExpression(arrow_function) => Some((
|
||||
FunctionLikeBody::Arrow(&arrow_function.body),
|
||||
&property_definition.key,
|
||||
)),
|
||||
Expression::FunctionExpression(function_expression) => Some((
|
||||
FunctionLikeBody::Function(function_expression.body.as_deref()?),
|
||||
&property_definition.key,
|
||||
)),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
@@ -248,7 +259,10 @@ impl Rule for ClassMethodsUseThis {
|
||||
return;
|
||||
}
|
||||
let mut finder = ThisFinder::new();
|
||||
finder.visit_function_body(function_body);
|
||||
match function_body {
|
||||
FunctionLikeBody::Function(body) => finder.visit_function_body(body),
|
||||
FunctionLikeBody::Arrow(body) => finder.visit_arrow_function_body(body),
|
||||
}
|
||||
if !finder.has_this {
|
||||
ctx.diagnostic(class_methods_use_this_diagnostic(name.span(), name.name()));
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ impl FuncStyle {
|
||||
|
||||
fn arrow_contains_this_or_super(arrow: &ArrowFunctionExpression) -> bool {
|
||||
let mut finder = ThisOrSuperFinder { found: false };
|
||||
finder.visit_function_body(&arrow.body);
|
||||
finder.visit_arrow_function_body(&arrow.body);
|
||||
finder.found
|
||||
}
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ impl GetterReturn {
|
||||
// const fn = () => 1;
|
||||
// ```
|
||||
if let AstKind::ArrowFunctionExpression(arrow_expr) = node.kind() {
|
||||
if arrow_expr.expression {
|
||||
if arrow_expr.is_expression() {
|
||||
break 'returns true;
|
||||
}
|
||||
// If the signature of function supports the return of the `undefined` value,
|
||||
|
||||
@@ -56,16 +56,6 @@ impl Rule for NoNew {
|
||||
let Some(node) = outermost_paren_parent(node, ctx) else { return };
|
||||
|
||||
if matches!(node.kind(), AstKind::ExpressionStatement(_)) {
|
||||
let mut ancestors = ctx
|
||||
.nodes()
|
||||
.ancestors(node.id())
|
||||
.filter(|a| !matches!(a.kind(), AstKind::ParenthesizedExpression(_)));
|
||||
ancestors.next(); // skip `FunctionBody`
|
||||
if let Some(node) = ancestors.next()
|
||||
&& matches!(node.kind(), AstKind::ArrowFunctionExpression(e) if e.expression)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let span = Span::new(expr.span.start, expr.callee.span().end);
|
||||
ctx.diagnostic(no_new_diagnostic(span));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use oxc_ast::{
|
||||
use oxc_ast_visit::VisitJs;
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -155,10 +155,10 @@ impl Rule for NoPromiseExecutorReturn {
|
||||
if self.allow_void && expr.get_inner_expression().is_void() {
|
||||
return;
|
||||
}
|
||||
ctx.diagnostic(no_promise_executor_return_diagnostic(arrow.body.span));
|
||||
ctx.diagnostic(no_promise_executor_return_diagnostic(arrow.body.span()));
|
||||
} else {
|
||||
// Arrow function with block body: check for return statements
|
||||
self.check_function_body(&arrow.body, ctx);
|
||||
self.check_function_body(arrow.get_function_body().unwrap(), ctx);
|
||||
}
|
||||
}
|
||||
Expression::FunctionExpression(func) => {
|
||||
|
||||
@@ -105,7 +105,7 @@ impl Rule for NoReturnAssign {
|
||||
"Return statements should not contain an assignment.",
|
||||
));
|
||||
}
|
||||
AstKind::ArrowFunctionExpression(arrow) if arrow.expression => {
|
||||
AstKind::ArrowFunctionExpression(arrow) if arrow.is_expression() => {
|
||||
ctx.diagnostic(no_return_assign_diagnostic(
|
||||
assign.span(),
|
||||
"Arrow functions should not return an assignment.",
|
||||
|
||||
@@ -163,14 +163,11 @@ impl NoSequences {
|
||||
}
|
||||
|
||||
// Check for ArrowFunctionExpression body
|
||||
// In oxc's AST: SequenceExpr -> ParenthesizedExpr -> ExpressionStatement -> FunctionBody -> ArrowFunctionExpr
|
||||
// In oxc's AST: SequenceExpr -> ParenthesizedExpr -> ArrowFunctionExpr
|
||||
// Arrow body needs double parentheses because the first layer is syntactically required
|
||||
// (otherwise `() => a, b` would be parsed as `(() => a), b`)
|
||||
let is_arrow_body = matches!(cur.kind(), AstKind::ExpressionStatement(_))
|
||||
&& matches!(
|
||||
nodes.parent_node(nodes.parent_node(cur.id()).id()).kind(),
|
||||
AstKind::ArrowFunctionExpression(arrow) if arrow.expression
|
||||
);
|
||||
let is_arrow_body =
|
||||
matches!(cur.kind(), AstKind::ArrowFunctionExpression(arrow) if arrow.is_expression());
|
||||
|
||||
if is_arrow_body {
|
||||
// Arrow body needs at least 2 levels of parentheses
|
||||
|
||||
@@ -99,7 +99,7 @@ fn is_parent_arrow_function_expression<'a>(node: &AstNode<'a>, ctx: &LintContext
|
||||
return false;
|
||||
};
|
||||
|
||||
arrow_function_expression.expression
|
||||
arrow_function_expression.is_expression()
|
||||
}
|
||||
|
||||
impl NoUnusedExpressions {
|
||||
|
||||
@@ -250,17 +250,7 @@ fn is_value_context(parent: &AstNode, child: &AstNode, semantic: &Semantic<'_>)
|
||||
// The right-hand side of an assignment is always in a value context (being read for assignment)
|
||||
assign_expr.right.span().contains_inclusive(child.span())
|
||||
}
|
||||
AstKind::ExpressionStatement(_) => {
|
||||
let grandparent = semantic.nodes().parent_node(parent.id());
|
||||
if let AstKind::FunctionBody(_) = grandparent.kind()
|
||||
&& let AstKind::ArrowFunctionExpression(arrow) =
|
||||
semantic.nodes().parent_kind(grandparent.id())
|
||||
&& arrow.expression
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
AstKind::ArrowFunctionExpression(arrow) => arrow.is_expression(),
|
||||
AstKind::ParenthesizedExpression(_)
|
||||
| AstKind::TSAsExpression(_)
|
||||
| AstKind::TSSatisfiesExpression(_)
|
||||
|
||||
@@ -579,8 +579,7 @@ impl<'a> Symbol<'_, 'a> {
|
||||
}
|
||||
AstKind::Function(f) if f.is_declaration() => break,
|
||||
// implicit return in an arrow function
|
||||
AstKind::ArrowFunctionExpression(f) if f.expression =>
|
||||
{
|
||||
AstKind::ArrowFunctionExpression(f) if f.is_expression() => {
|
||||
return false;
|
||||
}
|
||||
AstKind::ReturnStatement(_) => {
|
||||
@@ -664,12 +663,7 @@ impl<'a> Symbol<'_, 'a> {
|
||||
AstKind::ReturnStatement(_) => return true,
|
||||
AstKind::ExpressionStatement(_) => {}
|
||||
AstKind::Function(f) if f.is_expression() => {}
|
||||
// note: intentionally not using
|
||||
// ArrowFunctionExpression::get_expression since it returns
|
||||
// `Some` even if
|
||||
// 1. there are more than one statements
|
||||
// 2. the expression is surrounded by braces
|
||||
AstKind::ArrowFunctionExpression(f) if f.expression => {
|
||||
AstKind::ArrowFunctionExpression(f) if f.is_expression() => {
|
||||
return true;
|
||||
}
|
||||
x if x.is_statement() => return false,
|
||||
|
||||
@@ -256,7 +256,7 @@ fn make_function_shorthand<'a>(
|
||||
}
|
||||
Either::Right(func) => {
|
||||
let next_token = ctx
|
||||
.find_prev_token_from(func.body.span.start, "=>")
|
||||
.find_prev_token_from(func.body.span().start, "=>")
|
||||
.map(|offset| offset + 2 /* "=>".len() */);
|
||||
let Some(arrow_token) = next_token else {
|
||||
return fixer.noop();
|
||||
@@ -373,7 +373,7 @@ fn check_longform_methods<'a>(
|
||||
if rule.avoid_explicit_return_arrows
|
||||
&& let Expression::ArrowFunctionExpression(func) = &property.value.without_parentheses()
|
||||
&& !arrow_uses_lexical_identifiers(ctx, func)
|
||||
&& !func.expression
|
||||
&& !func.is_expression()
|
||||
{
|
||||
make_function_shorthand(ctx, property, Either::Right(func));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use oxc_ast::{
|
||||
AstKind,
|
||||
ast::{
|
||||
ArrowFunctionExpression, AwaitExpression, ForOfStatement, Function, FunctionType,
|
||||
MethodDefinition, ObjectProperty, PropertyKey,
|
||||
ArrowFunctionExpression, AwaitExpression, ForOfStatement, Function, FunctionBody,
|
||||
FunctionType, MethodDefinition, ObjectProperty, PropertyKey,
|
||||
},
|
||||
};
|
||||
use oxc_ast_visit::{VisitJs, walk_js::walk_for_of_statement};
|
||||
@@ -87,9 +87,31 @@ declare_oxc_lint!(
|
||||
|
||||
impl Rule for RequireAwait {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
let AstKind::FunctionBody(body) = node.kind() else {
|
||||
match node.kind() {
|
||||
AstKind::ArrowFunctionExpression(func) => Self::check_arrow_expression(func, ctx),
|
||||
AstKind::FunctionBody(body) => Self::check_function_body(body, node, ctx),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequireAwait {
|
||||
fn check_arrow_expression(func: &ArrowFunctionExpression, ctx: &LintContext) {
|
||||
if !func.r#async {
|
||||
return;
|
||||
};
|
||||
}
|
||||
let Some(expression) = func.get_expression() else { return };
|
||||
let mut finder = AwaitFinder { found: false };
|
||||
finder.visit_expression(expression);
|
||||
if !finder.found {
|
||||
let need_delete_span = get_delete_span(ctx, func.span.start);
|
||||
ctx.diagnostic_with_dangerous_fix(require_await_diagnostic(func.span), |fixer| {
|
||||
fixer.delete_range(need_delete_span)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn check_function_body<'a>(body: &FunctionBody<'a>, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
if body.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -228,20 +228,19 @@ impl Rule for PreferEndingWithAnExpect {
|
||||
impl PreferEndingWithAnExpect {
|
||||
fn is_valid_last_statement<'a>(
|
||||
&self,
|
||||
function_body: &'a FunctionBody<'a>,
|
||||
function_body: FunctionArgument<'a>,
|
||||
ctx: &LintContext<'a>,
|
||||
) -> bool {
|
||||
let Some(statement) = function_body.statements.last() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let statement_expression = match statement {
|
||||
Statement::ExpressionStatement(expression_statement) => {
|
||||
let statement_expression = match function_body {
|
||||
FunctionArgument::Expression(expression) => expression,
|
||||
FunctionArgument::Body(function_body) => {
|
||||
let Some(Statement::ExpressionStatement(expression_statement)) =
|
||||
function_body.statements.last()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
&expression_statement.expression
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let call_expression = match &statement_expression {
|
||||
@@ -274,10 +273,21 @@ impl PreferEndingWithAnExpect {
|
||||
}
|
||||
}
|
||||
|
||||
fn function_argument<'a>(argument: &'a Argument<'a>) -> Option<&'a FunctionBody<'a>> {
|
||||
#[derive(Clone, Copy)]
|
||||
enum FunctionArgument<'a> {
|
||||
Body(&'a FunctionBody<'a>),
|
||||
Expression(&'a Expression<'a>),
|
||||
}
|
||||
|
||||
fn function_argument<'a>(argument: &'a Argument<'a>) -> Option<FunctionArgument<'a>> {
|
||||
match argument {
|
||||
Argument::ArrowFunctionExpression(array_fn) => Some(&array_fn.body),
|
||||
Argument::FunctionExpression(function) => function.body.as_ref().map(AsRef::as_ref),
|
||||
Argument::ArrowFunctionExpression(array_fn) => array_fn
|
||||
.get_expression()
|
||||
.map(FunctionArgument::Expression)
|
||||
.or_else(|| array_fn.get_function_body().map(FunctionArgument::Body)),
|
||||
Argument::FunctionExpression(function) => {
|
||||
function.body.as_deref().map(FunctionArgument::Body)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use oxc_ast::ast::{Expression, FunctionBody};
|
||||
use oxc_ast::ast::Expression;
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::NodeId;
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
fixer::RuleFix,
|
||||
rule::{DefaultRuleConfig, Rule},
|
||||
rules::shared::prefer_expect_assertions::{
|
||||
DOCUMENTATION, PreferExpectAssertionsConfig, PreferExpectAssertionsRuleImpl,
|
||||
CallbackBody, DOCUMENTATION, PreferExpectAssertionsConfig, PreferExpectAssertionsRuleImpl,
|
||||
resolve_expect_local_name, should_check,
|
||||
},
|
||||
utils::collect_possible_jest_call_node,
|
||||
@@ -98,7 +98,7 @@ impl PreferExpectAssertionsRuleImpl for PreferExpectAssertions {
|
||||
ctx.diagnostic_with_suggestions(have_expect_assertions(span, prefix), suggestions);
|
||||
}
|
||||
|
||||
fn should_check_node(&self, body: &FunctionBody<'_>, is_async: bool, prefix: &str) -> bool {
|
||||
fn should_check_node(&self, body: CallbackBody<'_>, is_async: bool, prefix: &str) -> bool {
|
||||
should_check(self.0.as_ref(), body, is_async, prefix)
|
||||
}
|
||||
}
|
||||
@@ -1045,6 +1045,10 @@ fn test() {
|
||||
});",
|
||||
Some(serde_json::json!([{ "onlyFunctionsWithExpectInCallback": true }])),
|
||||
),
|
||||
(
|
||||
r#"test("x", () => promise.then(() => expect(x).toBe(y)))"#,
|
||||
Some(serde_json::json!([{ "onlyFunctionsWithExpectInCallback": true }])),
|
||||
),
|
||||
(
|
||||
"it('responds ok', function () {
|
||||
client.get('/user', response => {
|
||||
|
||||
@@ -59,20 +59,11 @@ impl Rule for MissingThrow {
|
||||
|
||||
impl MissingThrow {
|
||||
fn has_missing_throw<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool {
|
||||
if !matches!(ctx.nodes().parent_kind(node.id()), AstKind::ExpressionStatement(_)) {
|
||||
return false;
|
||||
match ctx.nodes().parent_kind(node.id()) {
|
||||
AstKind::ArrowFunctionExpression(arrow) => !arrow.is_expression(),
|
||||
AstKind::ExpressionStatement(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
|
||||
let expression_statement_id = ctx.nodes().parent_id(node.id());
|
||||
let expression_statement_parent_id = ctx.nodes().parent_id(expression_statement_id);
|
||||
if !matches!(ctx.nodes().kind(expression_statement_parent_id), AstKind::FunctionBody(_)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
!matches!(
|
||||
ctx.nodes().parent_kind(expression_statement_parent_id),
|
||||
AstKind::ArrowFunctionExpression(arrow_expr) if arrow_expr.expression
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -589,31 +589,26 @@ where
|
||||
F: FnMut(Spread<'a, '_>),
|
||||
{
|
||||
fn iter_spreads(ctx: &'ctx LintContext<'a>, map_cb: &Expression<'a>, cb: F) -> Option<Self> {
|
||||
let (mut visitor, body) = match map_cb {
|
||||
let mut visitor = match map_cb {
|
||||
Expression::ArrowFunctionExpression(f) => {
|
||||
let v = Self {
|
||||
let mut visitor = Self {
|
||||
ctx,
|
||||
cb,
|
||||
cb_scope_id: f.scope_id(),
|
||||
is_in_return: f.expression,
|
||||
return_span: f.expression.then(|| f.body.span()),
|
||||
is_in_return: f.is_expression(),
|
||||
return_span: f.is_expression().then(|| f.body.span()),
|
||||
};
|
||||
(v, f.body.as_ref())
|
||||
visitor.visit_arrow_function_body(&f.body);
|
||||
return Some(visitor);
|
||||
}
|
||||
Expression::FunctionExpression(f) => {
|
||||
let v = Self {
|
||||
ctx,
|
||||
cb,
|
||||
cb_scope_id: f.scope_id(),
|
||||
is_in_return: false,
|
||||
return_span: None,
|
||||
};
|
||||
let body = f.body.as_ref().map(AsRef::as_ref)?;
|
||||
(v, body)
|
||||
Self { ctx, cb, cb_scope_id: f.scope_id(), is_in_return: false, return_span: None }
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let Expression::FunctionExpression(function) = map_cb else { unreachable!() };
|
||||
let body = function.body.as_deref()?;
|
||||
visitor.visit_function_body(body);
|
||||
Some(visitor)
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ impl Rule for AlwaysReturn {
|
||||
|
||||
fn is_function_with_block_statement(node: &AstNode) -> bool {
|
||||
matches!(node.kind(), AstKind::Function(function_expr) if !function_expr.declare) // e.g. function () {}
|
||||
|| matches!(node.kind(), AstKind::ArrowFunctionExpression(arrow_func_expr) if !arrow_func_expr.expression) // e.g. () => {}
|
||||
|| matches!(node.kind(), AstKind::ArrowFunctionExpression(arrow_func_expr) if !arrow_func_expr.is_expression()) // e.g. () => {}
|
||||
}
|
||||
|
||||
fn is_member_call(node: &AstNode, member_name: &str) -> bool {
|
||||
@@ -383,20 +383,30 @@ fn has_ignored_assignment(
|
||||
node: &AstNode,
|
||||
ignore_assignment_variable: &FxHashSet<Cow<str>>,
|
||||
) -> bool {
|
||||
let is_ignored_assignment = |expression: &Expression| {
|
||||
let Expression::AssignmentExpression(assignment_expr) = expression else { return false };
|
||||
let object_name = get_root_object_name(&assignment_expr.left);
|
||||
object_name.is_some_and(|name| ignore_assignment_variable.contains(name))
|
||||
};
|
||||
|
||||
if let AstKind::ArrowFunctionExpression(arrow) = node.kind()
|
||||
&& let Some(expression) = arrow.get_expression()
|
||||
{
|
||||
return is_ignored_assignment(expression);
|
||||
}
|
||||
|
||||
let body_statements = match node.kind() {
|
||||
AstKind::Function(func_expr) => func_expr.body.as_ref().map(|body| &body.statements),
|
||||
AstKind::ArrowFunctionExpression(arrow_func_expr) => Some(&arrow_func_expr.body.statements),
|
||||
AstKind::ArrowFunctionExpression(arrow_func_expr) => {
|
||||
arrow_func_expr.get_function_body().map(|body| &body.statements)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
body_statements.is_some_and(|statements| {
|
||||
statements.iter().any(|it| match it {
|
||||
Statement::ExpressionStatement(expression) => match &expression.expression {
|
||||
Expression::AssignmentExpression(assignment_expr) => {
|
||||
let object_name = get_root_object_name(&assignment_expr.left);
|
||||
object_name.is_some_and(|name| ignore_assignment_variable.contains(name))
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
Statement::ExpressionStatement(expression) => {
|
||||
is_ignored_assignment(&expression.expression)
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -293,7 +293,7 @@ fn is_arrow_function_expression_return(node: &AstNode, ctx: &LintContext) -> boo
|
||||
}
|
||||
|
||||
if let AstKind::ArrowFunctionExpression(arrow_func) = ctx.nodes().parent_kind(parent.id()) {
|
||||
return arrow_func.expression;
|
||||
return arrow_func.is_expression();
|
||||
}
|
||||
|
||||
false
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use oxc_allocator::ArenaBox;
|
||||
use oxc_ast::{
|
||||
AstKind,
|
||||
ast::{Expression, FunctionBody, Statement},
|
||||
@@ -68,7 +67,9 @@ impl Rule for NoReturnInFinally {
|
||||
};
|
||||
match arg_expr {
|
||||
Expression::ArrowFunctionExpression(arrow_expr) => {
|
||||
find_return_statement(&arrow_expr.body, ctx);
|
||||
if let Some(body) = arrow_expr.get_function_body() {
|
||||
find_return_statement(body, ctx);
|
||||
}
|
||||
}
|
||||
Expression::FunctionExpression(func_expr) => {
|
||||
let Some(func_body) = &func_expr.body else {
|
||||
@@ -82,7 +83,7 @@ impl Rule for NoReturnInFinally {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_return_statement<'a>(func_body: &ArenaBox<'_, FunctionBody<'a>>, ctx: &LintContext<'a>) {
|
||||
fn find_return_statement<'a>(func_body: &FunctionBody<'a>, ctx: &LintContext<'a>) {
|
||||
let Some(return_stmt) =
|
||||
func_body.statements.iter().find(|stmt| matches!(stmt, Statement::ReturnStatement(_)))
|
||||
else {
|
||||
|
||||
@@ -8,7 +8,7 @@ use oxc_ast::{
|
||||
AstKind,
|
||||
ast::{
|
||||
ArrowFunctionExpression, CallExpression, Expression, Function, FunctionBody,
|
||||
ReturnStatement, Statement,
|
||||
ReturnStatement,
|
||||
},
|
||||
};
|
||||
use oxc_ast_visit::VisitJs;
|
||||
@@ -222,14 +222,12 @@ fn check_arrow_cb_arg<'a>(
|
||||
allow_reject: bool,
|
||||
arrow_expr: &ArrowFunctionExpression<'a>,
|
||||
) {
|
||||
if arrow_expr.expression {
|
||||
if let Some(Statement::ExpressionStatement(expr_stmt)) = arrow_expr.body.statements.first()
|
||||
&& let Expression::CallExpression(call_expr) = &expr_stmt.expression
|
||||
{
|
||||
if arrow_expr.is_expression() {
|
||||
if let Some(Expression::CallExpression(call_expr)) = arrow_expr.get_expression() {
|
||||
check_for_resolve_reject(ctx, allow_reject, call_expr);
|
||||
}
|
||||
} else {
|
||||
check_function_body(ctx, allow_reject, &arrow_expr.body);
|
||||
check_function_body(ctx, allow_reject, arrow_expr.get_function_body().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,15 +9,18 @@ use serde::Deserialize;
|
||||
use oxc_ast::{
|
||||
AstKind, AstType,
|
||||
ast::{
|
||||
Argument, ArrayExpressionElement, ArrowFunctionExpression, BindingPattern, CallExpression,
|
||||
ChainElement, Expression, FormalParameters, Function, FunctionBody, IdentifierReference,
|
||||
StaticMemberExpression, VariableDeclarationKind, VariableDeclarator,
|
||||
Argument, ArrayExpressionElement, ArrowFunctionBody, ArrowFunctionExpression,
|
||||
BindingPattern, CallExpression, ChainElement, Expression, FormalParameters, Function,
|
||||
FunctionBody, IdentifierReference, StaticMemberExpression, VariableDeclarationKind,
|
||||
VariableDeclarator,
|
||||
},
|
||||
match_expression,
|
||||
};
|
||||
use oxc_ast_visit::{
|
||||
VisitJs,
|
||||
walk_js::{walk_arrow_function_expression, walk_function, walk_function_body},
|
||||
walk_js::{
|
||||
walk_arrow_function_body, walk_arrow_function_expression, walk_function, walk_function_body,
|
||||
},
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
@@ -475,7 +478,7 @@ impl Rule for ExhaustiveDeps {
|
||||
found_dependencies.visit_formal_parameters(callback_node.parameters());
|
||||
|
||||
if let Some(function_body) = callback_node.body() {
|
||||
found_dependencies.visit_function_body(function_body);
|
||||
function_body.visit(&mut found_dependencies);
|
||||
}
|
||||
|
||||
(found_dependencies.found_dependencies, found_dependencies.refs_inside_cleanups)
|
||||
@@ -515,7 +518,7 @@ impl Rule for ExhaustiveDeps {
|
||||
let contains_set_state_call = {
|
||||
let mut finder = ExhaustiveDepsVisitor::new(ctx.semantic());
|
||||
if let Some(function_body) = callback_node.body() {
|
||||
finder.visit_function_body_root(function_body);
|
||||
function_body.visit_root(&mut finder);
|
||||
}
|
||||
finder.set_state_call
|
||||
};
|
||||
@@ -793,6 +796,28 @@ enum CallbackNode<'a> {
|
||||
ArrowFunction(&'a ArrowFunctionExpression<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum CallbackBody<'a, 'b> {
|
||||
Function(&'b FunctionBody<'a>),
|
||||
Arrow(&'b ArrowFunctionBody<'a>),
|
||||
}
|
||||
|
||||
impl<'a> CallbackBody<'a, '_> {
|
||||
fn visit(self, visitor: &mut ExhaustiveDepsVisitor<'a, '_>) {
|
||||
match self {
|
||||
Self::Function(body) => visitor.visit_function_body(body),
|
||||
Self::Arrow(body) => visitor.visit_arrow_function_body(body),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_root(self, visitor: &mut ExhaustiveDepsVisitor<'a, '_>) {
|
||||
match self {
|
||||
Self::Function(body) => walk_function_body(visitor, body),
|
||||
Self::Arrow(body) => walk_arrow_function_body(visitor, body),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CallbackNode<'a> {
|
||||
fn is_async(&self) -> bool {
|
||||
match self {
|
||||
@@ -808,10 +833,10 @@ impl<'a> CallbackNode<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn body(&self) -> Option<&FunctionBody<'a>> {
|
||||
fn body(&self) -> Option<CallbackBody<'a, '_>> {
|
||||
match self {
|
||||
CallbackNode::Function(func) => func.body.as_deref(),
|
||||
CallbackNode::ArrowFunction(func) => Some(&func.body),
|
||||
CallbackNode::Function(func) => func.body.as_deref().map(CallbackBody::Function),
|
||||
CallbackNode::ArrowFunction(func) => Some(CallbackBody::Arrow(&func.body)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1082,8 +1107,12 @@ fn is_stable_value<'a, 'b>(
|
||||
{
|
||||
// if the variables is a function, check whether the function is stable
|
||||
let function_body = match init.get_inner_expression() {
|
||||
Expression::ArrowFunctionExpression(arrow_func) => Some(&arrow_func.body),
|
||||
Expression::FunctionExpression(func) => func.body.as_ref(),
|
||||
Expression::ArrowFunctionExpression(arrow_func) => {
|
||||
Some(CallbackBody::Arrow(&arrow_func.body))
|
||||
}
|
||||
Expression::FunctionExpression(func) => {
|
||||
func.body.as_deref().map(CallbackBody::Function)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(function_body) = function_body {
|
||||
@@ -1167,8 +1196,10 @@ fn is_stable_value<'a, 'b>(
|
||||
}
|
||||
AstKind::ArrowFunctionExpression(_) | AstKind::Function(_) => {
|
||||
let function_body = match node.kind() {
|
||||
AstKind::ArrowFunctionExpression(arrow_func) => Some(&arrow_func.body),
|
||||
AstKind::Function(func) => func.body.as_ref(),
|
||||
AstKind::ArrowFunctionExpression(arrow_func) => {
|
||||
Some(CallbackBody::Arrow(&arrow_func.body))
|
||||
}
|
||||
AstKind::Function(func) => func.body.as_deref().map(CallbackBody::Function),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
@@ -1181,7 +1212,7 @@ fn is_stable_value<'a, 'b>(
|
||||
}
|
||||
|
||||
fn is_function_stable<'a, 'b>(
|
||||
function_body: &'b FunctionBody<'a>,
|
||||
function_body: CallbackBody<'a, 'b>,
|
||||
function_symbol_id: Option<SymbolId>,
|
||||
ctx: &'b LintContext<'a>,
|
||||
component_scope_id: ScopeId,
|
||||
@@ -1189,7 +1220,7 @@ fn is_function_stable<'a, 'b>(
|
||||
) -> bool {
|
||||
let deps = {
|
||||
let mut collector = ExhaustiveDepsVisitor::new(ctx.semantic());
|
||||
collector.visit_function_body(function_body);
|
||||
function_body.visit(&mut collector);
|
||||
collector.found_dependencies
|
||||
};
|
||||
|
||||
@@ -1257,10 +1288,6 @@ impl<'a, 'b> ExhaustiveDepsVisitor<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_function_body_root(&mut self, function_body: &FunctionBody<'a>) {
|
||||
walk_function_body(self, function_body);
|
||||
}
|
||||
|
||||
fn iter_destructure_bindings<F>(&self, mut cb: F) -> Option<bool>
|
||||
where
|
||||
F: FnMut(std::borrow::Cow<'a, str>),
|
||||
|
||||
@@ -562,8 +562,8 @@ mod fix {
|
||||
.return_type
|
||||
.as_ref()
|
||||
.map_or_else(String::new, |item| ctx.source_range(item.span).to_string()),
|
||||
body: ctx.source_range(arrow.body.span).to_string(),
|
||||
expression_body: arrow.expression,
|
||||
body: ctx.source_range(arrow.body.span()).to_string(),
|
||||
expression_body: arrow.is_expression(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,12 @@ use oxc_ast::{
|
||||
AstKind,
|
||||
ast::{
|
||||
ArrowFunctionExpression, Expression, JSXAttributeName, JSXAttributeValue, JSXElementName,
|
||||
JSXExpression, JSXMemberExpression, JSXMemberExpressionObject, Statement,
|
||||
StaticMemberExpression,
|
||||
JSXExpression, JSXMemberExpression, JSXMemberExpressionObject, StaticMemberExpression,
|
||||
},
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_str::{CompactStr, Ident};
|
||||
use schemars::{
|
||||
JsonSchema, SchemaGenerator,
|
||||
@@ -304,7 +303,7 @@ impl Rule for JsxHandlerNames {
|
||||
{
|
||||
(Some(Cow::Borrowed(name.as_str())), span, is_props_handler)
|
||||
} else {
|
||||
(None, arrow_function.body.span, false)
|
||||
(None, arrow_function.body.span(), false)
|
||||
}
|
||||
}
|
||||
JSXExpression::Identifier(ident) => {
|
||||
@@ -387,10 +386,7 @@ impl JsxHandlerNames {
|
||||
/// true if the expression is in the form of "foo.bar" or "() => foo.bar()"
|
||||
/// like event handler methods in class components.
|
||||
fn is_member_expression_callee(arrow_function: &ArrowFunctionExpression<'_>) -> bool {
|
||||
let Some(Statement::ExpressionStatement(stmt)) = arrow_function.body.statements.first() else {
|
||||
return false;
|
||||
};
|
||||
let Expression::CallExpression(callee_expr) = &stmt.expression else {
|
||||
let Some(Expression::CallExpression(callee_expr)) = arrow_function.get_expression() else {
|
||||
return false;
|
||||
};
|
||||
callee_expr.callee.is_member_expression()
|
||||
@@ -475,16 +471,13 @@ fn test_normalize_handler_name() {
|
||||
fn get_event_handler_name_from_arrow_function<'a>(
|
||||
arrow_function: &'a ArrowFunctionExpression<'a>,
|
||||
) -> Option<(Ident<'a>, Span, bool)> {
|
||||
if !arrow_function.expression {
|
||||
if !arrow_function.is_expression() {
|
||||
// Ignore arrow functions with block bodies like `() => { this.handleChange() }`.
|
||||
// The event handler name can only be extracted from arrow functions
|
||||
// with a single expression body, such as `() => this.handleChange()`.
|
||||
return None;
|
||||
}
|
||||
let Some(Statement::ExpressionStatement(stmt)) = arrow_function.body.statements.first() else {
|
||||
return None;
|
||||
};
|
||||
let Expression::CallExpression(call_expr) = &stmt.expression else {
|
||||
let Some(Expression::CallExpression(call_expr)) = arrow_function.get_expression() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use oxc_ast::{
|
||||
ast::{
|
||||
ArrayExpression, ArrayExpressionElement, CallExpression, Expression, IdentifierReference,
|
||||
JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXChild, JSXElement, JSXExpression,
|
||||
JSXFragment, Statement,
|
||||
JSXFragment,
|
||||
},
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
@@ -258,10 +258,7 @@ fn is_in_array_or_iter<'a, 'b>(
|
||||
let parent = ctx.nodes().parent_node(node.id());
|
||||
match parent.kind() {
|
||||
AstKind::ArrowFunctionExpression(arrow_expr) => {
|
||||
let is_arrow_expr_statement = matches!(
|
||||
arrow_expr.body.statements.first(),
|
||||
Some(Statement::ExpressionStatement(_))
|
||||
);
|
||||
let is_arrow_expr_statement = arrow_expr.is_expression();
|
||||
if !is_explicit_return && !is_arrow_expr_statement {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ use crate::{
|
||||
rule::{DefaultRuleConfig, Rule},
|
||||
rules::ContextHost,
|
||||
utils::{
|
||||
expression_contains_jsx, function_body_contains_jsx, function_contains_jsx, is_hoc_call,
|
||||
is_react_component_name,
|
||||
arrow_function_body_contains_jsx, expression_contains_jsx, function_contains_jsx,
|
||||
is_hoc_call, is_react_component_name,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -299,7 +299,7 @@ fn is_hoc_component(call: &CallExpression, ctx: &LintContext) -> bool {
|
||||
!is_passthrough_function(func) && function_contains_jsx(func)
|
||||
}
|
||||
Argument::ArrowFunctionExpression(arrow) => {
|
||||
!is_passthrough_arrow(arrow) && function_body_contains_jsx(&arrow.body)
|
||||
!is_passthrough_arrow(arrow) && arrow_function_body_contains_jsx(&arrow.body)
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
@@ -346,7 +346,9 @@ fn is_passthrough_arrow(arrow: &oxc_ast::ast::ArrowFunctionExpression) -> bool {
|
||||
// Expression arrow: `() => <Comp {...props} />`
|
||||
arrow.get_expression().is_some_and(is_simple_jsx_passthrough)
|
||||
// Block body with single return: `() => { return <Comp {...props} />; }`
|
||||
|| is_single_return_passthrough(&arrow.body.statements)
|
||||
|| arrow
|
||||
.get_function_body()
|
||||
.is_some_and(|body| is_single_return_passthrough(&body.statements))
|
||||
}
|
||||
|
||||
/// Check if statements consist of a single return with a simple JSX passthrough
|
||||
@@ -391,7 +393,7 @@ fn is_function_returning_null(expr: &Expression) -> bool {
|
||||
return expr.is_null();
|
||||
}
|
||||
// `() => { return null; }`
|
||||
arrow.body.statements.iter().any(|stmt| {
|
||||
arrow.get_function_body().unwrap().statements.iter().any(|stmt| {
|
||||
matches!(stmt, Statement::ReturnStatement(ret) if ret.argument.as_ref().is_some_and(Expression::is_null))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ impl Rule for NoRenderReturnValue {
|
||||
| AstKind::ObjectProperty(_)
|
||||
| AstKind::ReturnStatement(_)
|
||||
| AstKind::AssignmentExpression(_)
|
||||
| AstKind::ArrowFunctionExpression(_)
|
||||
) {
|
||||
ctx.diagnostic(no_render_return_value_diagnostic(ident.span.merge(property_span)));
|
||||
}
|
||||
@@ -83,7 +84,7 @@ impl Rule for NoRenderReturnValue {
|
||||
if ctx.scoping().scope_flags(scope_id).is_arrow()
|
||||
&& let AstKind::ArrowFunctionExpression(e) =
|
||||
ctx.nodes().kind(ctx.scoping().get_node_id(scope_id))
|
||||
&& e.expression
|
||||
&& e.is_expression()
|
||||
{
|
||||
ctx.diagnostic(no_render_return_value_diagnostic(ident.span.merge(property_span)));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
context::{ContextHost, LintContext},
|
||||
rule::{DefaultRuleConfig, Rule},
|
||||
utils::{
|
||||
expression_contains_jsx, function_body_contains_jsx, function_contains_jsx,
|
||||
arrow_function_body_contains_jsx, expression_contains_jsx, function_contains_jsx,
|
||||
is_create_element_call, is_es6_component, is_hoc_call, is_react_component_name,
|
||||
is_react_hook,
|
||||
},
|
||||
@@ -216,7 +216,7 @@ impl NoUnstableNestedComponents {
|
||||
return None;
|
||||
}
|
||||
|
||||
let contains_jsx = function_body_contains_jsx(&arrow.body);
|
||||
let contains_jsx = arrow_function_body_contains_jsx(&arrow.body);
|
||||
if !contains_jsx {
|
||||
return None;
|
||||
}
|
||||
@@ -326,7 +326,7 @@ fn find_parent_component_name(
|
||||
}
|
||||
AstKind::ArrowFunctionExpression(arrow) => {
|
||||
if is_first_argument_of_hoc_call(ancestor, ctx)
|
||||
|| !function_body_contains_jsx(&arrow.body)
|
||||
|| !arrow_function_body_contains_jsx(&arrow.body)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -583,7 +583,7 @@ fn is_hoc_component_call(call: &CallExpression<'_>, ctx: &LintContext<'_>) -> bo
|
||||
fn argument_contains_jsx(arg: &Argument<'_>, ctx: &LintContext<'_>) -> bool {
|
||||
match arg {
|
||||
Argument::FunctionExpression(func) => function_contains_jsx(func),
|
||||
Argument::ArrowFunctionExpression(arrow) => function_body_contains_jsx(&arrow.body),
|
||||
Argument::ArrowFunctionExpression(arrow) => arrow_function_body_contains_jsx(&arrow.body),
|
||||
Argument::CallExpression(call) => is_hoc_component_call(call, ctx),
|
||||
_ => arg.as_expression().is_some_and(expression_contains_jsx),
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ fn contains_return_statement(node: &AstNode, ctx: &LintContext) -> bool {
|
||||
&mut |basic_block_id, _state_going_into_this_rule| {
|
||||
// If its an arrow function with an expression, marked as founded and stop walking.
|
||||
if let AstKind::ArrowFunctionExpression(arrow_expr) = node.kind()
|
||||
&& arrow_expr.expression
|
||||
&& arrow_expr.is_expression()
|
||||
{
|
||||
return (FoundReturn::Yes, STOP_WALKING_ON_THIS_PATH);
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ impl<'a, 'b> AssertionVisitor<'a, 'b> {
|
||||
}
|
||||
}
|
||||
Expression::ArrowFunctionExpression(arrow_expr) => {
|
||||
self.visit_function_body(&arrow_expr.body);
|
||||
self.visit_arrow_function_body(&arrow_expr.body);
|
||||
}
|
||||
Expression::CallExpression(call_expr) => {
|
||||
self.visit_call_expression(call_expr);
|
||||
|
||||
@@ -127,7 +127,10 @@ fn is_correct_place_to_call_expect<'a>(
|
||||
let mut current = ctx.nodes().parent_node(current_node.id());
|
||||
|
||||
// loop until find the closest function body
|
||||
while !matches!(current.kind(), AstKind::FunctionBody(_) | AstKind::Program(_)) {
|
||||
while !matches!(
|
||||
current.kind(),
|
||||
AstKind::FunctionBody(_) | AstKind::ArrowFunctionExpression(_) | AstKind::Program(_)
|
||||
) {
|
||||
current = ctx.nodes().parent_node(current.id());
|
||||
}
|
||||
|
||||
@@ -136,7 +139,11 @@ fn is_correct_place_to_call_expect<'a>(
|
||||
return None;
|
||||
}
|
||||
|
||||
let parent = ctx.nodes().parent_node(current.id());
|
||||
let parent = if matches!(current.kind(), AstKind::ArrowFunctionExpression(_)) {
|
||||
current
|
||||
} else {
|
||||
ctx.nodes().parent_node(current.id())
|
||||
};
|
||||
|
||||
match parent.kind() {
|
||||
AstKind::Function(function) => {
|
||||
|
||||
@@ -105,10 +105,8 @@ pub fn run_on_jest_node<'a, 'c>(jest_node: &PossibleJestNode<'a, 'c>, ctx: &'c L
|
||||
/// Returns `None` if the function body doesn't contain exactly one await of a call expression.
|
||||
fn get_awaited_call_span_from_arrow(arrow: &oxc_ast::ast::ArrowFunctionExpression) -> Option<Span> {
|
||||
// Case 1: Arrow function with expression body (async () => await doSomething())
|
||||
if arrow.expression {
|
||||
if let Some(first) = arrow.body.statements.first()
|
||||
&& let Statement::ExpressionStatement(expr_stmt) = first
|
||||
&& let Expression::AwaitExpression(await_expr) = &expr_stmt.expression
|
||||
if arrow.is_expression() {
|
||||
if let Some(Expression::AwaitExpression(await_expr)) = arrow.get_expression()
|
||||
&& let Expression::CallExpression(call) = &await_expr.argument
|
||||
{
|
||||
return Some(call.span);
|
||||
@@ -117,7 +115,7 @@ fn get_awaited_call_span_from_arrow(arrow: &oxc_ast::ast::ArrowFunctionExpressio
|
||||
}
|
||||
|
||||
// Case 2: Arrow function with block body
|
||||
get_awaited_call_span_from_block(&arrow.body)
|
||||
get_awaited_call_span_from_block(arrow.get_function_body().unwrap())
|
||||
}
|
||||
|
||||
fn get_awaited_call_span_from_block(body: &oxc_ast::ast::FunctionBody) -> Option<Span> {
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
};
|
||||
use oxc_ast::{
|
||||
AstKind,
|
||||
ast::{Argument, CallExpression, Expression, FunctionBody, Statement},
|
||||
ast::{Argument, ArrowFunctionBody, CallExpression, Expression, FunctionBody, Statement},
|
||||
};
|
||||
use oxc_ast_visit::VisitJs;
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
@@ -28,6 +28,33 @@ pub struct PreferExpectAssertionsConfig {
|
||||
pub only_functions_with_expect_in_loop: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CallbackBody<'a> {
|
||||
Function(&'a FunctionBody<'a>),
|
||||
Expression(&'a Expression<'a>),
|
||||
}
|
||||
|
||||
impl<'a> CallbackBody<'a> {
|
||||
fn visit<V: VisitJs<'a>>(self, visitor: &mut V) {
|
||||
match self {
|
||||
Self::Function(body) => visitor.visit_function_body(body),
|
||||
Self::Expression(expression) => visitor.visit_expression(expression),
|
||||
}
|
||||
}
|
||||
|
||||
fn first_expression(self) -> Option<&'a Expression<'a>> {
|
||||
match self {
|
||||
Self::Function(body) => {
|
||||
let Statement::ExpressionStatement(statement) = body.statements.first()? else {
|
||||
return None;
|
||||
};
|
||||
Some(&statement.expression)
|
||||
}
|
||||
Self::Expression(expression) => Some(expression),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PreferExpectAssertionsConfig {
|
||||
#[inline]
|
||||
pub fn compute_config(&self) -> bool {
|
||||
@@ -232,7 +259,7 @@ pub trait PreferExpectAssertionsRuleImpl {
|
||||
};
|
||||
|
||||
let mut scanner = HookScanner::new(file_expect_prefix);
|
||||
scanner.visit_function_body(body);
|
||||
body.visit(&mut scanner);
|
||||
|
||||
if !scanner.has_expect_has_assertions {
|
||||
return;
|
||||
@@ -301,26 +328,43 @@ pub trait PreferExpectAssertionsRuleImpl {
|
||||
if Self::check_first_statement(body, prefix, ctx) {
|
||||
return;
|
||||
}
|
||||
let insert_pos = Span::new(body.span.start + 1, body.span.start + 1);
|
||||
let fixer = RuleFixer::new(FixKind::Suggestion, ctx);
|
||||
let suggestions = [
|
||||
fixer
|
||||
.insert_text_before_range(insert_pos, format!("{prefix}.hasAssertions();"))
|
||||
.with_message(format!("Add `{prefix}.hasAssertions()`")),
|
||||
fixer
|
||||
.insert_text_before_range(insert_pos, format!("{prefix}.assertions();"))
|
||||
.with_message(format!("Add `{prefix}.assertions(<number of assertions>)`")),
|
||||
];
|
||||
let suggestions = match body {
|
||||
CallbackBody::Function(body) => {
|
||||
let insert_pos = Span::new(body.span.start + 1, body.span.start + 1);
|
||||
[
|
||||
fixer
|
||||
.insert_text_before_range(insert_pos, format!("{prefix}.hasAssertions();"))
|
||||
.with_message(format!("Add `{prefix}.hasAssertions()`")),
|
||||
fixer
|
||||
.insert_text_before_range(insert_pos, format!("{prefix}.assertions();"))
|
||||
.with_message(format!("Add `{prefix}.assertions(<number of assertions>)")),
|
||||
]
|
||||
}
|
||||
CallbackBody::Expression(expression) => {
|
||||
let source = fixer.source_range(expression.span());
|
||||
[
|
||||
fixer
|
||||
.replace(
|
||||
expression.span(),
|
||||
format!("{{{prefix}.hasAssertions();return {source};}}"),
|
||||
)
|
||||
.with_message(format!("Add `{prefix}.hasAssertions()`")),
|
||||
fixer
|
||||
.replace(
|
||||
expression.span(),
|
||||
format!("{{{prefix}.assertions();return {source};}}"),
|
||||
)
|
||||
.with_message(format!("Add `{prefix}.assertions(<number of assertions>)")),
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
self.report_have_expect_assertions(call_expr.span, prefix, suggestions, ctx);
|
||||
}
|
||||
|
||||
fn check_first_statement(body: &FunctionBody<'_>, prefix: &str, ctx: &LintContext<'_>) -> bool {
|
||||
let Some(Statement::ExpressionStatement(first_expr_stmt)) = body.statements.first() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Expression::CallExpression(first_call) = &first_expr_stmt.expression else {
|
||||
fn check_first_statement(body: CallbackBody<'_>, prefix: &str, ctx: &LintContext<'_>) -> bool {
|
||||
let Some(Expression::CallExpression(first_call)) = body.first_expression() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -352,7 +396,7 @@ pub trait PreferExpectAssertionsRuleImpl {
|
||||
ctx: &LintContext<'_>,
|
||||
);
|
||||
|
||||
fn should_check_node(&self, body: &FunctionBody<'_>, is_async: bool, prefix: &str) -> bool;
|
||||
fn should_check_node(&self, body: CallbackBody<'_>, is_async: bool, prefix: &str) -> bool;
|
||||
}
|
||||
|
||||
fn is_covered_by_hook(
|
||||
@@ -374,7 +418,7 @@ fn is_covered_by_hook(
|
||||
|
||||
pub fn should_check(
|
||||
config: &PreferExpectAssertionsConfig,
|
||||
body: &FunctionBody<'_>,
|
||||
body: CallbackBody<'_>,
|
||||
is_async: bool,
|
||||
prefix: &str,
|
||||
) -> bool {
|
||||
@@ -388,7 +432,7 @@ pub fn should_check(
|
||||
}
|
||||
|
||||
let mut scanner = BodyScanner::new(prefix);
|
||||
scanner.visit_function_body(body);
|
||||
scanner.visit_callback_body(body);
|
||||
|
||||
let has_callback =
|
||||
config.only_functions_with_expect_in_callback && scanner.has_expect_in_callback;
|
||||
@@ -459,6 +503,17 @@ impl BodyScanner {
|
||||
self.in_loop = was;
|
||||
}
|
||||
|
||||
fn visit_callback_body(&mut self, body: CallbackBody<'_>) {
|
||||
match body {
|
||||
CallbackBody::Function(body) => self.visit_function_body(body),
|
||||
CallbackBody::Expression(expression) => {
|
||||
self.expression_depth += 1;
|
||||
self.visit_expression(expression);
|
||||
self.expression_depth -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_expect_call(&self, call_expr: &CallExpression<'_>) -> bool {
|
||||
let name = get_node_name(&call_expr.callee);
|
||||
name == self.prefix.as_str() || name.starts_with(self.prefix_dot.as_str())
|
||||
@@ -484,6 +539,16 @@ impl<'a> VisitJs<'a> for BodyScanner {
|
||||
self.expression_depth -= 1;
|
||||
}
|
||||
|
||||
fn visit_arrow_function_body(&mut self, body: &ArrowFunctionBody<'a>) {
|
||||
if body.is_expression() {
|
||||
self.expression_depth += 1;
|
||||
oxc_ast_visit::walk_js::walk_arrow_function_body(self, body);
|
||||
self.expression_depth -= 1;
|
||||
} else {
|
||||
oxc_ast_visit::walk_js::walk_arrow_function_body(self, body);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_for_statement(&mut self, it: &oxc_ast::ast::ForStatement<'a>) {
|
||||
self.visit_loop(|s| oxc_ast_visit::walk_js::walk_for_statement(s, it));
|
||||
}
|
||||
@@ -567,10 +632,13 @@ fn find_test_callback<'a>(call_expr: &'a CallExpression<'a>) -> Option<&'a Expre
|
||||
})
|
||||
}
|
||||
|
||||
fn callback_body<'a>(callback: &'a Expression<'a>) -> Option<&'a FunctionBody<'a>> {
|
||||
fn callback_body<'a>(callback: &'a Expression<'a>) -> Option<CallbackBody<'a>> {
|
||||
match callback {
|
||||
Expression::FunctionExpression(func) => func.body.as_ref().map(AsRef::as_ref),
|
||||
Expression::ArrowFunctionExpression(func) => Some(&func.body),
|
||||
Expression::FunctionExpression(func) => func.body.as_deref().map(CallbackBody::Function),
|
||||
Expression::ArrowFunctionExpression(func) => func
|
||||
.get_expression()
|
||||
.map(CallbackBody::Expression)
|
||||
.or_else(|| func.get_function_body().map(CallbackBody::Function)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,13 +83,11 @@ pub fn run<'a>(node: &oxc_semantic::AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
if !arrow_func.params.is_empty() {
|
||||
return;
|
||||
}
|
||||
let Some(stmt) = arrow_func.body.statements.first() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(expr) = arrow_func.get_expression() {
|
||||
report(is_once, property_span, Some(arrow_func.span), expr, ctx);
|
||||
} else if let Statement::ReturnStatement(return_stmt) = stmt {
|
||||
} else if let Some(Statement::ReturnStatement(return_stmt)) =
|
||||
arrow_func.get_function_body().unwrap().statements.first()
|
||||
{
|
||||
let Some(arg_expr) = &return_stmt.argument else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -162,17 +162,21 @@ fn is_mutable(symbol_id: SymbolId, ctx: &LintContext<'_>) -> bool {
|
||||
fn get_mock_return<'a>(argument_expression: &'a Expression<'a>) -> Option<&'a Expression<'a>> {
|
||||
match argument_expression {
|
||||
Expression::ArrowFunctionExpression(arrow_func) => {
|
||||
if arrow_func.r#async
|
||||
|| arrow_func.body.statements.len() > 1
|
||||
|| arrow_func.params.has_parameter()
|
||||
{
|
||||
if arrow_func.r#async || arrow_func.params.has_parameter() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let stmt = arrow_func.body.statements.first()?;
|
||||
if let Some(expression) = arrow_func.get_expression() {
|
||||
return Some(expression);
|
||||
}
|
||||
|
||||
let body = arrow_func.get_function_body()?;
|
||||
if body.statements.len() > 1 {
|
||||
return None;
|
||||
}
|
||||
let stmt = body.statements.first()?;
|
||||
|
||||
match stmt {
|
||||
Statement::ExpressionStatement(stmt_expr) => Some(&stmt_expr.expression),
|
||||
Statement::ReturnStatement(return_statement) => {
|
||||
let Some(arg_expr) = &return_statement.argument else {
|
||||
return None;
|
||||
|
||||
@@ -172,9 +172,12 @@ impl RequireHookConfig {
|
||||
}
|
||||
}
|
||||
Argument::ArrowFunctionExpression(arrow_func_expr)
|
||||
if !arrow_func_expr.expression =>
|
||||
if !arrow_func_expr.is_expression() =>
|
||||
{
|
||||
self.check_block_body(&arrow_func_expr.body.statements, ctx);
|
||||
self.check_block_body(
|
||||
&arrow_func_expr.get_function_body().unwrap().statements,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -116,17 +116,14 @@ pub fn run<'a>(
|
||||
diagnostic(ctx, arrow_expr.span, Message::UnexpectedDescribeArgument);
|
||||
}
|
||||
|
||||
if arrow_expr.expression && !arrow_expr.body.statements.is_empty() {
|
||||
let stmt = &arrow_expr.body.statements[0];
|
||||
let Statement::ExpressionStatement(expr_stmt) = stmt else {
|
||||
return;
|
||||
};
|
||||
if let Expression::CallExpression(call_expr) = &expr_stmt.expression {
|
||||
diagnostic(ctx, call_expr.span, Message::UnexpectedReturnInDescribe);
|
||||
}
|
||||
if arrow_expr.is_expression()
|
||||
&& let Some(Expression::CallExpression(call_expr)) = arrow_expr.get_expression()
|
||||
{
|
||||
diagnostic(ctx, call_expr.span, Message::UnexpectedReturnInDescribe);
|
||||
}
|
||||
|
||||
if let Some(span) = find_first_return_stmt_span(&arrow_expr.body) {
|
||||
if let Some(span) = arrow_expr.get_function_body().and_then(find_first_return_stmt_span)
|
||||
{
|
||||
diagnostic(ctx, span, Message::UnexpectedReturnInDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ fn is_acceptable_return_node<'a, 'b>(
|
||||
| AstKind::FunctionBody(_) => {
|
||||
node = ctx.nodes().parent_node(node.id());
|
||||
}
|
||||
AstKind::ArrowFunctionExpression(arrow_expr) => return arrow_expr.expression,
|
||||
AstKind::ArrowFunctionExpression(arrow_expr) => return arrow_expr.is_expression(),
|
||||
AstKind::AwaitExpression(_) => return true,
|
||||
_ => return false,
|
||||
}
|
||||
|
||||
@@ -291,10 +291,10 @@ fn is_top_level_promise_chain(expr: &Expression) -> bool {
|
||||
fn get_checkable_callback_body<'a>(callback: &'a Argument<'a>) -> Option<&'a FunctionBody<'a>> {
|
||||
match callback {
|
||||
Argument::ArrowFunctionExpression(arrow) => {
|
||||
if arrow.expression || !arrow.params.items.is_empty() {
|
||||
if arrow.is_expression() || !arrow.params.items.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(&arrow.body)
|
||||
arrow.get_function_body()
|
||||
}
|
||||
Argument::FunctionExpression(func) => {
|
||||
if !func.params.items.is_empty() {
|
||||
|
||||
@@ -300,7 +300,7 @@ impl ExplicitFunctionReturnType {
|
||||
if !self.allow_concise_arrow_function_expressions_starting_with_void {
|
||||
return false;
|
||||
}
|
||||
if !func.expression {
|
||||
if !func.is_expression() {
|
||||
return false;
|
||||
}
|
||||
let Some(expr) = func.get_expression() else { return false };
|
||||
@@ -597,7 +597,7 @@ fn ancestor_has_return_type<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bo
|
||||
|
||||
if let AstKind::ObjectProperty(prop) = parent.kind()
|
||||
&& let Expression::ArrowFunctionExpression(func) = &prop.value
|
||||
&& !func.body.statements.is_empty()
|
||||
&& func.get_function_body().is_some_and(|body| !body.statements.is_empty())
|
||||
&& func.return_type.is_some()
|
||||
{
|
||||
return true;
|
||||
@@ -631,7 +631,10 @@ fn ancestor_has_return_type<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bo
|
||||
|
||||
fn all_return_statements_are_functions(node: &AstNode) -> bool {
|
||||
let function_body = match node.kind() {
|
||||
AstKind::ArrowFunctionExpression(arrow_func_expr) => &arrow_func_expr.body,
|
||||
AstKind::ArrowFunctionExpression(arrow_func_expr) => {
|
||||
let Some(body) = arrow_func_expr.get_function_body() else { return false };
|
||||
body
|
||||
}
|
||||
AstKind::Function(func) => {
|
||||
if let Some(func_body) = &func.body {
|
||||
func_body
|
||||
|
||||
@@ -509,7 +509,7 @@ impl<'a, 'c> ExplicitTypesChecker<'a, 'c> {
|
||||
return;
|
||||
}
|
||||
|
||||
if arrow.expression {
|
||||
if arrow.is_expression() {
|
||||
let Some(expr) = arrow.get_expression() else {
|
||||
debug_assert!(
|
||||
false,
|
||||
@@ -535,7 +535,7 @@ impl<'a, 'c> ExplicitTypesChecker<'a, 'c> {
|
||||
// `export const foo = () => () => (): number => 1`
|
||||
Expression::ArrowFunctionExpression(_) | Expression::FunctionExpression(_) => {
|
||||
debug_assert!(self.rule.allow_higher_order_functions);
|
||||
walk_js::walk_function_body(self, &arrow.body);
|
||||
walk_js::walk_arrow_function_body(self, &arrow.body);
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
@@ -545,7 +545,7 @@ impl<'a, 'c> ExplicitTypesChecker<'a, 'c> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
walk_js::walk_function_body(self, &arrow.body);
|
||||
walk_js::walk_arrow_function_body(self, &arrow.body);
|
||||
|
||||
// AST is immutable in linter, so `unstable_address` produces stable `Address`es
|
||||
let is_hof = self.is_higher_order_function(arrow.unstable_address());
|
||||
|
||||
+8
-2
@@ -191,9 +191,15 @@ fn get_assignments_inside_expression<'a>(
|
||||
Expression::CallExpression(call) => {
|
||||
// Immediately Invoked Function Expression (IIFE)
|
||||
|
||||
if let Expression::ArrowFunctionExpression(expr) = call.callee.without_parentheses()
|
||||
&& let Some(Expression::AssignmentExpression(assignment)) = expr.get_expression()
|
||||
{
|
||||
assignments.push(assignment);
|
||||
}
|
||||
|
||||
let function_body = match call.callee.without_parentheses() {
|
||||
Expression::ArrowFunctionExpression(expr) => Some(&expr.body),
|
||||
Expression::FunctionExpression(expr) => expr.body.as_ref(),
|
||||
Expression::ArrowFunctionExpression(expr) => expr.get_function_body(),
|
||||
Expression::FunctionExpression(expr) => expr.body.as_deref(),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
||||
@@ -165,6 +165,11 @@ impl Rule for ConsistentFunctionScoping {
|
||||
}
|
||||
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
enum FunctionLikeBody<'a, 'b> {
|
||||
Function(&'b FunctionBody<'a>),
|
||||
Arrow(&'b ArrowFunctionBody<'a>),
|
||||
}
|
||||
|
||||
let (
|
||||
function_declaration_symbol_id,
|
||||
function_name,
|
||||
@@ -200,7 +205,7 @@ impl Rule for ConsistentFunctionScoping {
|
||||
(
|
||||
binding_ident.symbol_id(),
|
||||
Some(binding_ident.name.as_str()),
|
||||
function_body,
|
||||
FunctionLikeBody::Function(function_body),
|
||||
function.id.as_ref().map_or(
|
||||
Span::sized(function.span.start, 8),
|
||||
|func_binding_ident| func_binding_ident.span,
|
||||
@@ -211,7 +216,7 @@ impl Rule for ConsistentFunctionScoping {
|
||||
(
|
||||
function_id.symbol_id(),
|
||||
Some(function_id.name.as_str()),
|
||||
function_body,
|
||||
FunctionLikeBody::Function(function_body),
|
||||
function_id.span(),
|
||||
func_scope_id,
|
||||
)
|
||||
@@ -227,7 +232,7 @@ impl Rule for ConsistentFunctionScoping {
|
||||
(
|
||||
binding_ident.symbol_id(),
|
||||
Some(binding_ident.name.as_str()),
|
||||
&arrow_function.body,
|
||||
FunctionLikeBody::Arrow(&arrow_function.body),
|
||||
binding_ident.span(),
|
||||
arrow_function.scope_id(),
|
||||
)
|
||||
@@ -258,7 +263,10 @@ impl Rule for ConsistentFunctionScoping {
|
||||
// get all references in the function body
|
||||
let (function_body_var_references, is_parent_this_referenced) = {
|
||||
let mut rf = ReferencesFinder::default();
|
||||
rf.visit_function_body(function_body);
|
||||
match function_body {
|
||||
FunctionLikeBody::Function(body) => rf.visit_function_body(body),
|
||||
FunctionLikeBody::Arrow(body) => rf.visit_arrow_function_body(body),
|
||||
}
|
||||
(rf.references, rf.is_parent_this_referenced)
|
||||
};
|
||||
|
||||
|
||||
@@ -111,14 +111,19 @@ fn is_simple_operation(node: &CallExpression) -> bool {
|
||||
let Some(callback_arg) = node.arguments.first() else {
|
||||
return false;
|
||||
};
|
||||
if let Argument::ArrowFunctionExpression(callback) = callback_arg
|
||||
&& let Some(expression) = callback.get_expression()
|
||||
{
|
||||
return matches!(expression, Expression::BinaryExpression(_));
|
||||
}
|
||||
let function_body = match callback_arg {
|
||||
// `array.reduce((accumulator, element) => accumulator + element)`
|
||||
Argument::ArrowFunctionExpression(callback) => &callback.body,
|
||||
Argument::ArrowFunctionExpression(callback) => callback.get_function_body().unwrap(),
|
||||
Argument::FunctionExpression(callback) => {
|
||||
let Some(body) = &callback.body else {
|
||||
return false;
|
||||
};
|
||||
body
|
||||
body.as_ref()
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
@@ -519,7 +519,10 @@ fn expression_references_variable(expr: &Expression<'_>, var_name: &str) -> bool
|
||||
return false;
|
||||
}
|
||||
// Check the function body for references
|
||||
arrow.body.statements.iter().any(|stmt| {
|
||||
if let Some(expression) = arrow.get_expression() {
|
||||
return expression_references_variable(expression, var_name);
|
||||
}
|
||||
arrow.get_function_body().unwrap().statements.iter().any(|stmt| {
|
||||
if let Statement::ExpressionStatement(expr_stmt) = stmt {
|
||||
expression_references_variable(&expr_stmt.expression, var_name)
|
||||
} else if let Statement::ReturnStatement(ret) = stmt {
|
||||
|
||||
@@ -108,7 +108,7 @@ impl Rule for NoInvalidRemoveEventListener {
|
||||
Span::new(func_expr.span.start, func_expr.params.span.end)
|
||||
}
|
||||
Argument::ArrowFunctionExpression(arrow_expr) => {
|
||||
Span::new(arrow_expr.span.start, arrow_expr.body.span.start)
|
||||
Span::new(arrow_expr.span.start, arrow_expr.body.span().start)
|
||||
}
|
||||
Argument::CallExpression(_) => listener_span,
|
||||
_ => unreachable!(),
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use oxc_ast::{
|
||||
AstKind,
|
||||
ast::{Expression, Statement},
|
||||
};
|
||||
use oxc_ast::{AstKind, ast::Expression};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{AstNode, context::LintContext, rule::Rule};
|
||||
|
||||
@@ -67,14 +64,12 @@ impl Rule for NoUnreadableIife {
|
||||
return;
|
||||
};
|
||||
|
||||
if !arrow_expr.expression {
|
||||
if !arrow_expr.is_expression() {
|
||||
return;
|
||||
}
|
||||
let Statement::ExpressionStatement(expr_stmt) = &arrow_expr.body.statements[0] else {
|
||||
return;
|
||||
};
|
||||
if matches!(expr_stmt.expression, Expression::ParenthesizedExpression(_)) {
|
||||
ctx.diagnostic(no_unreadable_iife_diagnostic(expr_stmt.span));
|
||||
let Some(expression) = arrow_expr.get_expression() else { return };
|
||||
if matches!(expression, Expression::ParenthesizedExpression(_)) {
|
||||
ctx.diagnostic(no_unreadable_iife_diagnostic(expression.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,16 +271,6 @@ fn is_bind_member_expression(node: &AstNode) -> bool {
|
||||
member_expr.static_property_name().is_some_and(|name| name == "bind")
|
||||
}
|
||||
|
||||
fn match_arrow_function_body<'a>(ctx: &LintContext<'a>, parent: &AstNode<'a>) -> bool {
|
||||
let parent = ctx.nodes().parent_node(parent.id());
|
||||
if !matches!(parent.kind(), AstKind::FunctionBody(_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let grand_parent = ctx.nodes().parent_node(parent.id());
|
||||
matches!(grand_parent.kind(), AstKind::ArrowFunctionExpression(_))
|
||||
}
|
||||
|
||||
fn generate_fix<'a>(
|
||||
call_expr: &CallExpression,
|
||||
is_reject: bool,
|
||||
@@ -325,10 +315,8 @@ fn generate_fix<'a>(
|
||||
return fixer.noop();
|
||||
};
|
||||
|
||||
let is_arrow_function_body = match parent.kind() {
|
||||
AstKind::ExpressionStatement(_) => match_arrow_function_body(ctx, parent),
|
||||
_ => false,
|
||||
};
|
||||
let is_arrow_function_body =
|
||||
matches!(parent.kind(), AstKind::ArrowFunctionExpression(arrow) if arrow.is_expression());
|
||||
|
||||
let mut replace_range = if is_reject { parent.kind().span() } else { call_expr.span() };
|
||||
let replacement_text = if is_reject {
|
||||
@@ -342,7 +330,7 @@ fn generate_fix<'a>(
|
||||
text = format!("{text};");
|
||||
// `=> Promise.reject(error)` -> `=> { throw error; }`
|
||||
if is_arrow_function_body {
|
||||
replace_range = outermost_paren(parent, ctx).kind().span();
|
||||
replace_range = node.kind().span();
|
||||
text = format!("{{ {text} }}");
|
||||
}
|
||||
text
|
||||
|
||||
@@ -152,14 +152,13 @@ fn is_undefined(arg: &Argument) -> bool {
|
||||
}
|
||||
|
||||
fn is_has_function_return_type(node: &AstNode, ctx: &LintContext<'_>) -> bool {
|
||||
let parent_node = ctx.nodes().parent_node(node.id());
|
||||
match parent_node.kind() {
|
||||
match node.kind() {
|
||||
AstKind::Program(_) => false,
|
||||
AstKind::ArrowFunctionExpression(arrow_func_express) => {
|
||||
arrow_func_express.return_type.is_some()
|
||||
}
|
||||
AstKind::Function(func) => func.return_type.is_some(),
|
||||
_ => is_has_function_return_type(parent_node, ctx),
|
||||
_ => is_has_function_return_type(ctx.nodes().parent_node(node.id()), ctx),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,22 +209,10 @@ impl Rule for NoUselessUndefined {
|
||||
);
|
||||
}
|
||||
// `() => undefined`
|
||||
AstKind::ExpressionStatement(_) => {
|
||||
AstKind::ArrowFunctionExpression(arrow) if arrow.is_expression() => {
|
||||
if !self.check_arrow_function_body {
|
||||
return;
|
||||
}
|
||||
let grand_parent_node = ctx.nodes().parent_node(parent_node.id());
|
||||
let grand_parent_node_kind = grand_parent_node.kind();
|
||||
let AstKind::FunctionBody(func_body) = grand_parent_node_kind else {
|
||||
return;
|
||||
};
|
||||
let grand_grand_parent_node =
|
||||
ctx.nodes().parent_node(grand_parent_node.id());
|
||||
let grand_grand_parent_node_kind = grand_grand_parent_node.kind();
|
||||
let AstKind::ArrowFunctionExpression(_) = grand_grand_parent_node_kind
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
if is_has_function_return_type(parent_node, ctx) {
|
||||
return;
|
||||
@@ -233,7 +220,7 @@ impl Rule for NoUselessUndefined {
|
||||
|
||||
ctx.diagnostic_with_fix(
|
||||
no_useless_undefined_diagnostic(undefined_literal.span),
|
||||
|fixer| fixer.replace(func_body.span, "{}"),
|
||||
|fixer| fixer.replace(arrow.body.span(), "{}"),
|
||||
);
|
||||
}
|
||||
// `let foo = undefined` / `var foo = undefined`
|
||||
|
||||
Loaded 100 of 166 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user