Skip to content

Commit 33f6166

Browse files
committed
fix(formatter): correct printing comments within the type annotation of ArrayPattern and ObjectPattern
1 parent 76f9892 commit 33f6166

File tree

4 files changed

+109
-35
lines changed

4 files changed

+109
-35
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use std::ops::Deref;
2+
3+
use oxc_ast::ast::ArrayPattern;
4+
use oxc_span::{GetSpan, Span};
5+
6+
use crate::{
7+
Format, FormatResult,
8+
formatter::{Formatter, prelude::*, trivia::format_dangling_comments},
9+
generated::ast_nodes::{AstNode, AstNodes},
10+
utils::format_node_without_trailing_comments::FormatNodeWithoutTrailingComments,
11+
write,
12+
write::utils::array::write_array_node,
13+
};
14+
15+
use super::FormatWrite;
16+
17+
struct FormatArrayPattern<'a, 'b>(&'b AstNode<'a, ArrayPattern<'a>>);
18+
19+
impl<'a> Deref for FormatArrayPattern<'a, '_> {
20+
type Target = AstNode<'a, ArrayPattern<'a>>;
21+
22+
fn deref(&self) -> &Self::Target {
23+
self.0
24+
}
25+
}
26+
27+
impl GetSpan for FormatArrayPattern<'_, '_> {
28+
fn span(&self) -> Span {
29+
// `[a, b]: [a, b]`
30+
// ^^^^^^^^^^^^^^ ArrayPattern's span covers the type annotation if exists,
31+
// ^^^^^^ but we want the span to cover only the pattern itself, otherwise,
32+
// the comments of type annotation will be treated as dangling comments
33+
// of ArrayPattern.
34+
if let AstNodes::FormalParameter(param) = self.parent
35+
&& let Some(ty) = &param.pattern.type_annotation
36+
{
37+
Span::new(self.span.start, ty.span.start)
38+
} else {
39+
self.span
40+
}
41+
}
42+
}
43+
44+
impl<'a> Format<'a> for FormatArrayPattern<'a, '_> {
45+
fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
46+
write!(f, "[")?;
47+
48+
if self.elements.is_empty() && self.rest.is_none() {
49+
write!(f, [format_dangling_comments(self.span()).with_block_indent()])?;
50+
} else {
51+
write!(
52+
f,
53+
group(&soft_block_indent(&format_once(|f| {
54+
if !self.elements.is_empty() {
55+
write_array_node(
56+
self.elements.len() + usize::from(self.rest.is_some()),
57+
self.elements().iter().map(AstNode::as_ref),
58+
f,
59+
)?;
60+
}
61+
if let Some(rest) = self.rest() {
62+
write!(f, [soft_line_break_or_space(), rest]);
63+
}
64+
Ok(())
65+
})))
66+
)?;
67+
}
68+
69+
write!(f, "]")
70+
}
71+
}
72+
73+
impl<'a> FormatWrite<'a> for AstNode<'a, ArrayPattern<'a>> {
74+
fn write(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
75+
if matches!(self.parent, AstNodes::FormalParameter(param) if param.pattern.type_annotation.is_some())
76+
{
77+
FormatNodeWithoutTrailingComments(&FormatArrayPattern(self)).fmt(f)
78+
} else {
79+
FormatArrayPattern(self).fmt(f)
80+
}
81+
}
82+
}

crates/oxc_formatter/src/write/mod.rs

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod array_element_list;
22
mod array_expression;
3+
mod array_pattern;
34
mod arrow_function_expression;
45
mod as_or_satisfies_expression;
56
mod assignment_pattern_property_list;
@@ -871,7 +872,12 @@ impl<'a> FormatWrite<'a> for AstNode<'a, AssignmentPattern<'a>> {
871872

872873
impl<'a> FormatWrite<'a> for AstNode<'a, ObjectPattern<'a>> {
873874
fn write(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
874-
ObjectPatternLike::ObjectPattern(self).fmt(f)
875+
if matches!(self.parent, AstNodes::FormalParameter(param) if param.pattern.type_annotation.is_some())
876+
{
877+
FormatNodeWithoutTrailingComments(&ObjectPatternLike::ObjectPattern(self)).fmt(f)
878+
} else {
879+
ObjectPatternLike::ObjectPattern(self).fmt(f)
880+
}
875881
}
876882
}
877883

@@ -906,35 +912,6 @@ impl<'a> FormatWrite<'a> for AstNode<'a, BindingProperty<'a>> {
906912
}
907913
}
908914

909-
impl<'a> FormatWrite<'a> for AstNode<'a, ArrayPattern<'a>> {
910-
fn write(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
911-
write!(f, "[")?;
912-
913-
if self.elements.is_empty() && self.rest.is_none() {
914-
write!(f, [format_dangling_comments(self.span()).with_block_indent()])?;
915-
} else {
916-
write!(
917-
f,
918-
group(&soft_block_indent(&format_once(|f| {
919-
if !self.elements.is_empty() {
920-
write_array_node(
921-
self.elements.len() + usize::from(self.rest.is_some()),
922-
self.elements().iter().map(AstNode::as_ref),
923-
f,
924-
)?;
925-
}
926-
if let Some(rest) = self.rest() {
927-
write!(f, [soft_line_break_or_space(), rest]);
928-
}
929-
Ok(())
930-
})))
931-
)?;
932-
}
933-
934-
write!(f, "]")
935-
}
936-
}
937-
938915
impl<'a> FormatWrite<'a> for AstNode<'a, BindingRestElement<'a>> {
939916
fn write(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
940917
write!(f, ["...", self.argument()])

crates/oxc_formatter/src/write/object_pattern_like.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use oxc_ast::ast::*;
2+
use oxc_span::GetSpan;
23

34
use crate::{
45
formatter::{
@@ -21,14 +22,29 @@ pub enum ObjectPatternLike<'a, 'b> {
2122
ObjectAssignmentTarget(&'b AstNode<'a, ObjectAssignmentTarget<'a>>),
2223
}
2324

24-
impl<'a> ObjectPatternLike<'a, '_> {
25+
impl GetSpan for ObjectPatternLike<'_, '_> {
2526
fn span(&self) -> Span {
2627
match self {
27-
Self::ObjectPattern(o) => o.span,
28-
Self::ObjectAssignmentTarget(o) => o.span,
28+
Self::ObjectPattern(node) => {
29+
// `{a, b}: {a: number, b: string}`
30+
// ^^^^^^^^^^^^^^ ObjectPattern's span covers the type annotation if exists,
31+
// ^^^^^^ but we want the span to cover only the pattern itself, otherwise,
32+
// the comments of type annotation will be treated as dangling comments
33+
// of ObjectPattern.
34+
if let AstNodes::FormalParameter(param) = node.parent
35+
&& let Some(ty) = &param.pattern.type_annotation
36+
{
37+
Span::new(node.span.start, ty.span.start)
38+
} else {
39+
node.span
40+
}
41+
}
42+
Self::ObjectAssignmentTarget(node) => node.span,
2943
}
3044
}
45+
}
3146

47+
impl<'a> ObjectPatternLike<'a, '_> {
3248
fn is_empty(&self) -> bool {
3349
match self {
3450
Self::ObjectPattern(o) => o.is_empty(),

tasks/prettier_conformance/snapshots/prettier.ts.snap.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ts compatibility: 527/573 (91.97%)
1+
ts compatibility: 528/573 (92.15%)
22

33
# Failed
44

@@ -19,7 +19,6 @@ ts compatibility: 527/573 (91.97%)
1919
| typescript/chain-expression/test.ts | 💥 | 0.00% |
2020
| typescript/class/empty-method-body.ts | 💥 | 80.00% |
2121
| typescript/class/quoted-property.ts | 💥 | 66.67% |
22-
| typescript/comments/location.ts | 💥 | 95.00% |
2322
| typescript/comments/method_types.ts | 💥 | 84.62% |
2423
| typescript/comments/type-parameters.ts | 💥 | 65.52% |
2524
| typescript/conditional-types/comments.ts | 💥✨ | 31.51% |

0 commit comments

Comments
 (0)