mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 18:43:44 +00:00
fix(formatter): keep parens around private in expressions (#28096)
Should keep:
```ts
class C {
#x;
m(a, b, c) {
x = (#x in b).c; // THIS parens with `PrivateInExpression`
```
This commit is contained in:
1 parent
db55461552
commit
a5a52cf5f7
4 files changed
+168
-25
No files matched your search
@@ -6,7 +6,7 @@ use oxc_span::GetSpan;
|
||||
use crate::{
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
formatter::{JsFormatter, JsFormatterExt as _},
|
||||
print::{BinaryLikeExpression, should_flatten, unary_argument_takes_comment_parens},
|
||||
print::{BinaryLikeOperator, should_flatten, unary_argument_takes_comment_parens},
|
||||
utils::{expression::ExpressionLeftSide, typecast::is_cast_target},
|
||||
};
|
||||
|
||||
@@ -393,11 +393,11 @@ impl NeedsParentheses<'_> for AstNode<'_, '_, BinaryExpression<'_>> {
|
||||
|
||||
// For `in` expressions in ForStatement: wrap to avoid ambiguity
|
||||
// e.g., `for (var a = (b in c);;)`
|
||||
if self.operator.is_in() && is_in_for_initializer(self) {
|
||||
if self.operator.is_in() && is_in_for_initializer(self.span, parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
binary_like_needs_parens(BinaryLikeExpression::BinaryExpression(self), f)
|
||||
binary_like_needs_parens(self.span, self.operator.into(), parent, f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,10 +407,8 @@ impl NeedsParentheses<'_> for AstNode<'_, '_, BinaryExpression<'_>> {
|
||||
/// separately at the VariableDeclarator level (see `is_for_in_statement_init`).
|
||||
///
|
||||
/// <https://github.com/prettier/prettier/issues/907#issuecomment-284304321>
|
||||
fn is_in_for_initializer(expr: &AstNode<'_, '_, BinaryExpression<'_>>) -> bool {
|
||||
let ancestors = expr.ancestors();
|
||||
|
||||
for parent in ancestors {
|
||||
fn is_in_for_initializer(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||
for parent in parent.ancestors() {
|
||||
match parent {
|
||||
AstNodes::ExpressionStatement(stmt) => {
|
||||
// Block body: `() => { expr; }` or `function() { expr; }` - continue checking
|
||||
@@ -422,10 +420,7 @@ fn is_in_for_initializer(expr: &AstNode<'_, '_, BinaryExpression<'_>>) -> bool {
|
||||
return false;
|
||||
}
|
||||
AstNodes::ForStatement(stmt) => {
|
||||
return stmt
|
||||
.init
|
||||
.as_ref()
|
||||
.is_some_and(|init| init.span().contains_inclusive(expr.span));
|
||||
return stmt.init.as_ref().is_some_and(|init| init.span().contains_inclusive(span));
|
||||
}
|
||||
// ForInStatement is handled at VariableDeclarator level, not here
|
||||
AstNodes::ForInStatement(_) | AstNodes::Program(_) => return false,
|
||||
@@ -464,8 +459,10 @@ impl NeedsParentheses<'_> for AstNode<'_, '_, PrivateInExpression<'_>> {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_class_extends(self.span, self.parent())
|
||||
|| matches!(self.parent(), AstNodes::UnaryExpression(_))
|
||||
// Same rules as a binary `in` expression
|
||||
let parent = self.parent();
|
||||
is_in_for_initializer(self.span, parent)
|
||||
|| binary_like_needs_parens(self.span, BinaryOperator::In.into(), parent, f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +487,7 @@ impl NeedsParentheses<'_> for AstNode<'_, '_, LogicalExpression<'_>> {
|
||||
{
|
||||
true
|
||||
} else {
|
||||
binary_like_needs_parens(BinaryLikeExpression::LogicalExpression(self), f)
|
||||
binary_like_needs_parens(self.span, self.operator.into(), parent, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -513,6 +510,7 @@ impl NeedsParentheses<'_> for AstNode<'_, '_, ConditionalExpression<'_>> {
|
||||
| AstNodes::JSXSpreadAttribute(_)
|
||||
| AstNodes::LogicalExpression(_)
|
||||
| AstNodes::BinaryExpression(_)
|
||||
| AstNodes::PrivateInExpression(_)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -747,6 +745,7 @@ impl NeedsParentheses<'_> for AstNode<'_, '_, ArrowFunctionExpression<'_>> {
|
||||
| AstNodes::AwaitExpression(_)
|
||||
| AstNodes::LogicalExpression(_)
|
||||
| AstNodes::BinaryExpression(_)
|
||||
| AstNodes::PrivateInExpression(_)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -963,10 +962,12 @@ fn is_followed_by_comparison_or_sign(
|
||||
}
|
||||
|
||||
fn binary_like_needs_parens(
|
||||
binary_like: BinaryLikeExpression<'_, '_>,
|
||||
span: Span,
|
||||
operator: BinaryLikeOperator,
|
||||
parent: &AstNodes<'_, '_>,
|
||||
f: &JsFormatter<'_, '_>,
|
||||
) -> bool {
|
||||
let parent = match binary_like.parent() {
|
||||
let (parent_operator, parent_right): (BinaryLikeOperator, Span) = match parent {
|
||||
// NOTE: The unary adds the parentheses itself when comments are involved.
|
||||
// Deliberately binary-like-only: Prettier keeps the inner parentheses for every other argument type (`!((x, y) /* c */)`)
|
||||
// (Seems a leftover of prettier#18397's narrow scope, not a principle.)
|
||||
@@ -986,22 +987,23 @@ fn binary_like_needs_parens(
|
||||
| AstNodes::StaticMemberExpression(_)
|
||||
| AstNodes::TaggedTemplateExpression(_) => return true,
|
||||
AstNodes::ComputedMemberExpression(computed) => {
|
||||
return computed.object.span() == binary_like.span();
|
||||
return computed.object.span() == span;
|
||||
}
|
||||
AstNodes::Class(class) => {
|
||||
return class.heritage().is_some_and(|heritage| {
|
||||
let super_class = heritage.expression();
|
||||
super_class.span().contains_inclusive(binary_like.span())
|
||||
super_class.span().contains_inclusive(span)
|
||||
});
|
||||
}
|
||||
AstNodes::BinaryExpression(binary) => BinaryLikeExpression::BinaryExpression(binary),
|
||||
AstNodes::LogicalExpression(logical) => BinaryLikeExpression::LogicalExpression(logical),
|
||||
parent if parent.is_call_like_callee_span(binary_like.span()) => return true,
|
||||
AstNodes::BinaryExpression(binary) => (binary.operator.into(), binary.right.span()),
|
||||
AstNodes::LogicalExpression(logical) => (logical.operator.into(), logical.right.span()),
|
||||
AstNodes::PrivateInExpression(private_in) => {
|
||||
(BinaryOperator::In.into(), private_in.right.span())
|
||||
}
|
||||
parent if parent.is_call_like_callee_span(span) => return true,
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
let parent_operator = parent.operator();
|
||||
let operator = binary_like.operator();
|
||||
let parent_precedence = parent_operator.precedence();
|
||||
let precedence = operator.precedence();
|
||||
|
||||
@@ -1011,7 +1013,7 @@ fn binary_like_needs_parens(
|
||||
return true;
|
||||
}
|
||||
|
||||
let is_right = parent.right().span() == binary_like.span();
|
||||
let is_right = parent_right == span;
|
||||
|
||||
// `a ** b ** c`
|
||||
if is_right && parent_precedence == precedence {
|
||||
@@ -1187,6 +1189,7 @@ fn ts_as_or_satisfies_needs_parens(span: Span, parent: &AstNodes<'_, '_>) -> boo
|
||||
AstNodes::ConditionalExpression(_)
|
||||
| AstNodes::LogicalExpression(_)
|
||||
| AstNodes::BinaryExpression(_)
|
||||
| AstNodes::PrivateInExpression(_)
|
||||
) || type_cast_like_needs_parens(span, parent)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ mod variable_declaration;
|
||||
pub use arrow_function_expression::{
|
||||
FormatJsArrowFunctionExpression, FormatJsArrowFunctionExpressionOptions,
|
||||
};
|
||||
pub use binary_like_expression::{BinaryLikeExpression, should_flatten};
|
||||
pub use binary_like_expression::{BinaryLikeExpression, BinaryLikeOperator, should_flatten};
|
||||
pub use fragment::{FormatFunctionParams, FormatTypeParameters};
|
||||
pub use semicolon::write_comments_before_closing_paren;
|
||||
pub use template::embed_hug;
|
||||
|
||||
@@ -6,3 +6,38 @@ class F {
|
||||
+(#target in this);
|
||||
}
|
||||
}
|
||||
|
||||
// `#x in b` keeps the parens of a binary `in` expression
|
||||
class C {
|
||||
#x;
|
||||
m(a, b, c) {
|
||||
x = (#x in b).c;
|
||||
x = (#x in b)[c];
|
||||
x = (#x in b)();
|
||||
x = new (#x in b)();
|
||||
x = (#x in b)`t`;
|
||||
x = (#x in b)?.c;
|
||||
x = [...(#x in b)];
|
||||
x = a + (#x in b);
|
||||
x = (#x in b) + a;
|
||||
x = a < (#x in b);
|
||||
x = (#x in b) < a;
|
||||
x = a in (#x in b);
|
||||
x = (#x in b) & a;
|
||||
x = a ** (#x in b);
|
||||
x = (#x in b) && a;
|
||||
x = (#x in b) ? a : c;
|
||||
x = class extends (#x in b) {};
|
||||
for (let y = (#x in b); ; );
|
||||
|
||||
x = #x in (a < b);
|
||||
x = #x in (a in b);
|
||||
x = #x in (a + b);
|
||||
x = #x in (a && b);
|
||||
x = #x in (a ? b : c);
|
||||
x = #x in (() => a);
|
||||
}
|
||||
async n(b) {
|
||||
x = await (#x in b);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,41 @@ class F {
|
||||
}
|
||||
}
|
||||
|
||||
// `#x in b` keeps the parens of a binary `in` expression
|
||||
class C {
|
||||
#x;
|
||||
m(a, b, c) {
|
||||
x = (#x in b).c;
|
||||
x = (#x in b)[c];
|
||||
x = (#x in b)();
|
||||
x = new (#x in b)();
|
||||
x = (#x in b)`t`;
|
||||
x = (#x in b)?.c;
|
||||
x = [...(#x in b)];
|
||||
x = a + (#x in b);
|
||||
x = (#x in b) + a;
|
||||
x = a < (#x in b);
|
||||
x = (#x in b) < a;
|
||||
x = a in (#x in b);
|
||||
x = (#x in b) & a;
|
||||
x = a ** (#x in b);
|
||||
x = (#x in b) && a;
|
||||
x = (#x in b) ? a : c;
|
||||
x = class extends (#x in b) {};
|
||||
for (let y = (#x in b); ; );
|
||||
|
||||
x = #x in (a < b);
|
||||
x = #x in (a in b);
|
||||
x = #x in (a + b);
|
||||
x = #x in (a && b);
|
||||
x = #x in (a ? b : c);
|
||||
x = #x in (() => a);
|
||||
}
|
||||
async n(b) {
|
||||
x = await (#x in b);
|
||||
}
|
||||
}
|
||||
|
||||
==================== Output ====================
|
||||
------------------
|
||||
{ printWidth: 80 }
|
||||
@@ -24,6 +59,41 @@ class F {
|
||||
}
|
||||
}
|
||||
|
||||
// `#x in b` keeps the parens of a binary `in` expression
|
||||
class C {
|
||||
#x;
|
||||
m(a, b, c) {
|
||||
x = (#x in b).c;
|
||||
x = (#x in b)[c];
|
||||
x = (#x in b)();
|
||||
x = new (#x in b)();
|
||||
x = (#x in b)`t`;
|
||||
x = (#x in b)?.c;
|
||||
x = [...(#x in b)];
|
||||
x = a + (#x in b);
|
||||
x = (#x in b) + a;
|
||||
x = a < (#x in b);
|
||||
x = #x in b < a;
|
||||
x = a in (#x in b);
|
||||
x = (#x in b) & a;
|
||||
x = a ** (#x in b);
|
||||
x = #x in b && a;
|
||||
x = #x in b ? a : c;
|
||||
x = class extends (#x in b) {};
|
||||
for (let y = (#x in b); ;);
|
||||
|
||||
x = #x in (a < b);
|
||||
x = #x in (a in b);
|
||||
x = #x in a + b;
|
||||
x = #x in (a && b);
|
||||
x = #x in (a ? b : c);
|
||||
x = #x in (() => a);
|
||||
}
|
||||
async n(b) {
|
||||
x = await (#x in b);
|
||||
}
|
||||
}
|
||||
|
||||
-------------------
|
||||
{ printWidth: 100 }
|
||||
-------------------
|
||||
@@ -36,4 +106,39 @@ class F {
|
||||
}
|
||||
}
|
||||
|
||||
// `#x in b` keeps the parens of a binary `in` expression
|
||||
class C {
|
||||
#x;
|
||||
m(a, b, c) {
|
||||
x = (#x in b).c;
|
||||
x = (#x in b)[c];
|
||||
x = (#x in b)();
|
||||
x = new (#x in b)();
|
||||
x = (#x in b)`t`;
|
||||
x = (#x in b)?.c;
|
||||
x = [...(#x in b)];
|
||||
x = a + (#x in b);
|
||||
x = (#x in b) + a;
|
||||
x = a < (#x in b);
|
||||
x = #x in b < a;
|
||||
x = a in (#x in b);
|
||||
x = (#x in b) & a;
|
||||
x = a ** (#x in b);
|
||||
x = #x in b && a;
|
||||
x = #x in b ? a : c;
|
||||
x = class extends (#x in b) {};
|
||||
for (let y = (#x in b); ;);
|
||||
|
||||
x = #x in (a < b);
|
||||
x = #x in (a in b);
|
||||
x = #x in a + b;
|
||||
x = #x in (a && b);
|
||||
x = #x in (a ? b : c);
|
||||
x = #x in (() => a);
|
||||
}
|
||||
async n(b) {
|
||||
x = await (#x in b);
|
||||
}
|
||||
}
|
||||
|
||||
===================== End =====================
|
||||
Reference in new issue
Block a user