diff --git a/crates/oxc_formatter/src/parentheses/expression.rs b/crates/oxc_formatter/src/parentheses/expression.rs index c42440332f15d..a3bc6c73b5ba5 100644 --- a/crates/oxc_formatter/src/parentheses/expression.rs +++ b/crates/oxc_formatter/src/parentheses/expression.rs @@ -638,16 +638,44 @@ impl<'a> NeedsParentheses<'a> for AstNode<'a, TSSatisfiesExpression<'a>> { impl<'a> NeedsParentheses<'a> for AstNode<'a, TSTypeAssertion<'a>> { fn needs_parentheses(&self, f: &Formatter<'_, 'a>) -> bool { - matches!( - self.parent, - AstNodes::ComputedMemberExpression(_) - | AstNodes::StaticMemberExpression(_) - | AstNodes::PrivateFieldExpression(_) - | AstNodes::IdentifierReference(_) - | AstNodes::AssignmentExpression(_) - | AstNodes::AssignmentTargetWithDefault(_) - | AstNodes::UpdateExpression(_) - ) + match self.parent { + AstNodes::TSAsExpression(_) | AstNodes::TSSatisfiesExpression(_) => true, + AstNodes::BinaryExpression(binary) => { + matches!(binary.operator, BinaryOperator::ShiftLeft) + } + _ => type_cast_like_needs_parens(self.span(), self.parent), + } + } +} + +fn type_cast_like_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool { + #[expect(clippy::match_same_arms)] + match parent { + AstNodes::ExportDefaultDeclaration(_) + | AstNodes::TSTypeAssertion(_) + | AstNodes::UnaryExpression(_) + | AstNodes::AwaitExpression(_) + | AstNodes::TSNonNullExpression(_) + // Callee + | AstNodes::CallExpression(_) + | AstNodes::NewExpression(_) + // template tag + | AstNodes::TaggedTemplateExpression(_) + // in spread + | AstNodes::JSXSpreadChild(_) + | AstNodes::SpreadElement(_) + | AstNodes::JSXSpreadAttribute(_) + // static member + | AstNodes::StaticMemberExpression(_) => true, + AstNodes::ComputedMemberExpression(member) => { + member.object.span() == span + } + // assignment left hand side + AstNodes::UpdateExpression(_) | AstNodes::AssignmentTargetWithDefault(_) => true, + AstNodes::AssignmentExpression(assignment) => { + assignment.left.span() == span + } + _ => is_class_extends(span, parent), } } diff --git a/tasks/coverage/snapshots/formatter_typescript.snap b/tasks/coverage/snapshots/formatter_typescript.snap index afb15e9ebd0d1..32ab0f1165eed 100644 --- a/tasks/coverage/snapshots/formatter_typescript.snap +++ b/tasks/coverage/snapshots/formatter_typescript.snap @@ -2,15 +2,13 @@ commit: 261630d6 formatter_typescript Summary: AST Parsed : 8816/8816 (100.00%) -Positive Passed: 8774/8816 (99.52%) +Positive Passed: 8775/8816 (99.53%) Mismatch: tasks/coverage/typescript/tests/cases/compiler/amdLikeInputDeclarationEmit.ts Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/arrayFromAsync.ts `await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules`await` is only allowed within async functions and at the top levels of modules Mismatch: tasks/coverage/typescript/tests/cases/compiler/callOfConditionalTypeWithConcreteBranches.ts -Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/castExpressionParentheses.ts -Unexpected token Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/castFunctionExpressionShouldBeParenthesized.ts Expected a semicolon or an implicit semicolon after a statement, but found none Mismatch: tasks/coverage/typescript/tests/cases/compiler/coAndContraVariantInferences3.ts diff --git a/tasks/prettier_conformance/snapshots/prettier.ts.snap.md b/tasks/prettier_conformance/snapshots/prettier.ts.snap.md index 61a108ac0a00d..486daa7e81ad3 100644 --- a/tasks/prettier_conformance/snapshots/prettier.ts.snap.md +++ b/tasks/prettier_conformance/snapshots/prettier.ts.snap.md @@ -1,4 +1,4 @@ -ts compatibility: 386/573 (67.36%) +ts compatibility: 389/573 (67.89%) # Failed @@ -50,7 +50,6 @@ ts compatibility: 386/573 (67.36%) | typescript/comments/type_literals.ts | 💥 | 68.97% | | typescript/comments/union.ts | 💥 | 5.26% | | typescript/compiler/anyIsAssignableToObject.ts | 💥 | 75.00% | -| typescript/compiler/castOfAwait.ts | 💥 | 87.50% | | typescript/compiler/castTest.ts | 💥 | 96.67% | | typescript/compiler/contextualSignatureInstantiation2.ts | 💥 | 88.89% | | typescript/compiler/indexSignatureWithInitializer.ts | 💥 | 75.00% | @@ -68,7 +67,6 @@ ts compatibility: 386/573 (67.36%) | typescript/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMixedWithModifiers.ts | 💥 | 86.67% | | typescript/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingClass.ts | 💥 | 96.77% | | typescript/conformance/es6/Symbols/symbolProperty15.ts | 💥 | 73.33% | -| typescript/conformance/expressions/functionCalls/callWithSpreadES6.ts | 💥 | 97.96% | | typescript/conformance/types/ambient/ambientDeclarations.ts | 💥 | 90.00% | | typescript/conformance/types/functions/functionOverloadCompatibilityWithVoid01.ts | 💥 | 75.00% | | typescript/conformance/types/functions/functionOverloadCompatibilityWithVoid02.ts | 💥 | 75.00% | @@ -172,7 +170,6 @@ ts compatibility: 386/573 (67.36%) | typescript/type-alias/conditional.ts | 💥 | 23.33% | | typescript/type-alias/issue-100857.ts | 💥 | 67.61% | | typescript/type-alias/issue-9874.ts | 💥 | 0.00% | -| typescript/type-arguments-bit-shift-left-like/1.ts | 💥 | 0.00% | | typescript/type-arguments-bit-shift-left-like/3.ts | 💥 | 0.00% | | typescript/type-arguments-bit-shift-left-like/5.tsx | 💥 | 0.00% | | typescript/typeof/typeof.ts | 💥 | 25.00% |