mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(formatter): keep comments after TS this_param (#25459)
Fixes #25410 Syntactically, this `this` is written inside the function parameters. However, in the AST, `this_param` and `params` are adjacent fields, and since the inclusion relationship of the spans is broken, exception handling is required.
This commit is contained in:
1 parent
e3683f29d8
commit
b63ecccb1f
3 files changed
+171
-7
No files matched your search
@@ -8,7 +8,7 @@ use crate::{
|
||||
formatter::{
|
||||
JsFormatter,
|
||||
prelude::*,
|
||||
trivia::{FormatLeadingComments, FormatTrailingComments},
|
||||
trivia::{FormatLeadingComments, FormatTrailingComments, format_trailing_comments},
|
||||
},
|
||||
options::{FormatTrailingCommas, TrailingSeparator},
|
||||
utils::call_expression::{is_angular_test_wrapper, is_test_call_expression},
|
||||
@@ -17,6 +17,12 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
/// NOTE: In the AST, `this_param` is a sibling field of `FormalParameters`,
|
||||
/// but the `FormalParameters` span covers the whole parens INCLUDING `this`.
|
||||
///
|
||||
/// Span windows assuming source-ordered, non-overlapping fields are inverted here:
|
||||
/// the generated `following_span_start` for `this_param` (= params span start) points before it,
|
||||
/// so the generated trailing-comment pass never captures anything (see [`Parameter::This`]).
|
||||
pub fn get_this_param<'a>(parent: &AstNodes<'a>) -> Option<&'a AstNode<'a, TSThisParameter<'a>>> {
|
||||
match parent {
|
||||
AstNodes::Function(func) => func.this_param(),
|
||||
@@ -167,7 +173,10 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSThisParameter<'a>> {
|
||||
}
|
||||
|
||||
enum Parameter<'a, 'b> {
|
||||
This(&'b AstNode<'a, TSThisParameter<'a>>),
|
||||
This {
|
||||
param: &'b AstNode<'a, TSThisParameter<'a>>,
|
||||
list: &'b AstNode<'a, FormalParameters<'a>>,
|
||||
},
|
||||
Formal(&'b AstNode<'a, FormalParameter<'a>>),
|
||||
Rest(&'b AstNode<'a, FormalParameterRest<'a>>),
|
||||
}
|
||||
@@ -175,7 +184,7 @@ enum Parameter<'a, 'b> {
|
||||
impl GetSpan for Parameter<'_, '_> {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::This(param) => param.span(),
|
||||
Self::This { param, .. } => param.span(),
|
||||
Self::Formal(param) => param.span(),
|
||||
Self::Rest(e) => e.span(),
|
||||
}
|
||||
@@ -185,7 +194,19 @@ impl GetSpan for Parameter<'_, '_> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for Parameter<'a, '_> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
match self {
|
||||
Self::This(param) => param.fmt(f),
|
||||
Self::This { param, list } => {
|
||||
param.fmt(f);
|
||||
// The generated trailing pass captures nothing here (inverted window, see `get_this_param`).
|
||||
// Capture like any other parameter: up to the next one, or dangling inside the parens when `this` is last
|
||||
// (`AST_NODE_WITHOUT_FOLLOWING_NODE_LIST` rule in `ast_nodes.rs` generator gives the list's own last child).
|
||||
let following_span_start = list
|
||||
.items
|
||||
.first()
|
||||
.map(|p| p.span.start)
|
||||
.or_else(|| list.rest.as_deref().map(|r| r.span.start))
|
||||
.unwrap_or(0);
|
||||
format_trailing_comments(list.span(), param.span(), following_span_start).fmt(f);
|
||||
}
|
||||
Self::Formal(param) => param.fmt(f),
|
||||
Self::Rest(e) => e.fmt(f),
|
||||
}
|
||||
@@ -193,14 +214,18 @@ impl<'a> Format<'a, JsFormatContext<'a>> for Parameter<'a, '_> {
|
||||
}
|
||||
|
||||
struct FormalParametersIter<'a, 'b> {
|
||||
this: Option<&'b AstNode<'a, TSThisParameter<'a>>>,
|
||||
this: Option<Parameter<'a, 'b>>,
|
||||
params: AstNodeIterator<'a, FormalParameter<'a>>,
|
||||
rest: Option<&'b AstNode<'a, FormalParameterRest<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> From<&'b ParameterList<'a, 'b>> for FormalParametersIter<'a, 'b> {
|
||||
fn from(value: &'b ParameterList<'a, 'b>) -> Self {
|
||||
Self { this: value.this, params: value.list.items().iter(), rest: value.list.rest() }
|
||||
Self {
|
||||
this: value.this.map(|param| Parameter::This { param, list: value.list }),
|
||||
params: value.list.items().iter(),
|
||||
rest: value.list.rest(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +233,7 @@ impl<'a, 'b> Iterator for FormalParametersIter<'a, 'b> {
|
||||
type Item = Parameter<'a, 'b>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.this.take().map(Parameter::This).or_else(|| {
|
||||
self.this.take().or_else(|| {
|
||||
self.params
|
||||
.next()
|
||||
.map(Parameter::Formal)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Comments after a `this` parameter stay inside the parens (issue #25410)
|
||||
interface X {
|
||||
ownLine<T>(
|
||||
this: void
|
||||
// own-line comment
|
||||
): void;
|
||||
sameLine(
|
||||
this: void // same-line comment
|
||||
): void;
|
||||
block(
|
||||
this: void
|
||||
/* block comment */
|
||||
): void;
|
||||
beforeNext(
|
||||
this: void, // trailing this
|
||||
a: string
|
||||
): void;
|
||||
}
|
||||
type F = (
|
||||
this: void
|
||||
// fn type
|
||||
) => void;
|
||||
declare function g(
|
||||
this: void,
|
||||
...rest: number[]
|
||||
// after rest
|
||||
): void;
|
||||
function h(
|
||||
this: void
|
||||
// own line
|
||||
) {}
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
source: crates/oxc_formatter/tests/fixtures/mod.rs
|
||||
---
|
||||
==================== Input ====================
|
||||
// Comments after a `this` parameter stay inside the parens (issue #25410)
|
||||
interface X {
|
||||
ownLine<T>(
|
||||
this: void
|
||||
// own-line comment
|
||||
): void;
|
||||
sameLine(
|
||||
this: void // same-line comment
|
||||
): void;
|
||||
block(
|
||||
this: void
|
||||
/* block comment */
|
||||
): void;
|
||||
beforeNext(
|
||||
this: void, // trailing this
|
||||
a: string
|
||||
): void;
|
||||
}
|
||||
type F = (
|
||||
this: void
|
||||
// fn type
|
||||
) => void;
|
||||
declare function g(
|
||||
this: void,
|
||||
...rest: number[]
|
||||
// after rest
|
||||
): void;
|
||||
function h(
|
||||
this: void
|
||||
// own line
|
||||
) {}
|
||||
|
||||
==================== Output ====================
|
||||
------------------
|
||||
{ printWidth: 80 }
|
||||
------------------
|
||||
// Comments after a `this` parameter stay inside the parens (issue #25410)
|
||||
interface X {
|
||||
ownLine<T>(
|
||||
this: void,
|
||||
// own-line comment
|
||||
): void;
|
||||
sameLine(
|
||||
this: void, // same-line comment
|
||||
): void;
|
||||
block(
|
||||
this: void,
|
||||
/* block comment */
|
||||
): void;
|
||||
beforeNext(
|
||||
this: void, // trailing this
|
||||
a: string,
|
||||
): void;
|
||||
}
|
||||
type F = (
|
||||
this: void,
|
||||
// fn type
|
||||
) => void;
|
||||
declare function g(
|
||||
this: void,
|
||||
...rest: number[]
|
||||
// after rest
|
||||
): void;
|
||||
function h(
|
||||
this: void,
|
||||
// own line
|
||||
) {}
|
||||
|
||||
-------------------
|
||||
{ printWidth: 100 }
|
||||
-------------------
|
||||
// Comments after a `this` parameter stay inside the parens (issue #25410)
|
||||
interface X {
|
||||
ownLine<T>(
|
||||
this: void,
|
||||
// own-line comment
|
||||
): void;
|
||||
sameLine(
|
||||
this: void, // same-line comment
|
||||
): void;
|
||||
block(
|
||||
this: void,
|
||||
/* block comment */
|
||||
): void;
|
||||
beforeNext(
|
||||
this: void, // trailing this
|
||||
a: string,
|
||||
): void;
|
||||
}
|
||||
type F = (
|
||||
this: void,
|
||||
// fn type
|
||||
) => void;
|
||||
declare function g(
|
||||
this: void,
|
||||
...rest: number[]
|
||||
// after rest
|
||||
): void;
|
||||
function h(
|
||||
this: void,
|
||||
// own line
|
||||
) {}
|
||||
|
||||
===================== End =====================
|
||||
Reference in new issue
Block a user