mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
fix(linter): no-useless-spread fixer with multiple spread elements (#3950)
Closes #3909
This commit is contained in:
parent
ea30aecb67
commit
6498a089ea
1 changed files with 87 additions and 31 deletions
|
|
@ -147,43 +147,97 @@ fn check_useless_spread_in_list<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let AstKind::SpreadElement(spread_elem) = parent.kind() {
|
// we're in ...[]
|
||||||
let Some(parent_parent) = outermost_paren_parent(parent, ctx) else {
|
let AstKind::SpreadElement(spread_elem) = parent.kind() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
let Some(parent_parent) = outermost_paren_parent(parent, ctx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let span = Span::new(spread_elem.span.start, spread_elem.span.start + 3);
|
let span = Span::new(spread_elem.span.start, spread_elem.span.start + 3);
|
||||||
|
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
AstKind::ObjectExpression(_) => {
|
AstKind::ObjectExpression(_) => {
|
||||||
// { ...{ } }
|
// { ...{ } }
|
||||||
if matches!(parent_parent.kind(), AstKind::ObjectExpression(_)) {
|
if matches!(parent_parent.kind(), AstKind::ObjectExpression(_)) {
|
||||||
ctx.diagnostic(spread_in_list(span, "object"));
|
ctx.diagnostic(spread_in_list(span, "object"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AstKind::ArrayExpression(array_expr) => match parent_parent.kind() {
|
||||||
|
// ...[ ...[] ]
|
||||||
|
AstKind::ArrayExpressionElement(_) => {
|
||||||
|
let diagnostic = spread_in_list(span, "array");
|
||||||
|
if let Some(outer_array) = ctx.nodes().parent_kind(parent_parent.id()) {
|
||||||
|
diagnose_array_in_array_spread(ctx, diagnostic, &outer_array, array_expr);
|
||||||
|
} else {
|
||||||
|
ctx.diagnostic(diagnostic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AstKind::ArrayExpression(array_expr) => match parent_parent.kind() {
|
// foo(...[ ])
|
||||||
// ...[ ]
|
AstKind::Argument(_) => {
|
||||||
AstKind::ArrayExpressionElement(_) => {
|
ctx.diagnostic_with_fix(spread_in_arguments(span), |fixer| {
|
||||||
let diagnostic = spread_in_list(span, "array");
|
fix_by_removing_spread(fixer, array_expr, spread_elem)
|
||||||
if let Some(outer_array) = ctx.nodes().parent_kind(parent_parent.id()) {
|
});
|
||||||
ctx.diagnostic_with_fix(diagnostic, |fixer| {
|
}
|
||||||
fix_replace(fixer, &outer_array, array_expr)
|
_ => {}
|
||||||
});
|
},
|
||||||
} else {
|
_ => {
|
||||||
ctx.diagnostic(diagnostic);
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `...[ ...[] ]`. May contain multiple spread elements.
|
||||||
|
fn diagnose_array_in_array_spread<'a>(
|
||||||
|
ctx: &LintContext<'a>,
|
||||||
|
diagnostic: OxcDiagnostic,
|
||||||
|
outer_array: &AstKind<'a>,
|
||||||
|
inner_array: &ArrayExpression<'a>,
|
||||||
|
) {
|
||||||
|
let AstKind::ArrayExpression(outer_array) = outer_array else {
|
||||||
|
ctx.diagnostic(diagnostic);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
match outer_array.elements.len() {
|
||||||
|
0 => unreachable!(),
|
||||||
|
1 => {
|
||||||
|
ctx.diagnostic_with_fix(diagnostic, |fixer| {
|
||||||
|
fix_replace(fixer, &outer_array.span, inner_array)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// If all elements are array spreads, we can merge them all together
|
||||||
|
let mut spreads: Vec<&'a ArrayExpression> = vec![];
|
||||||
|
for el in &outer_array.elements {
|
||||||
|
let ArrayExpressionElement::SpreadElement(spread) = el else {
|
||||||
|
ctx.diagnostic(diagnostic);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Expression::ArrayExpression(arr) = &spread.argument else {
|
||||||
|
ctx.diagnostic(diagnostic);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
spreads.push(arr.as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
// [ ...[a, b, c], ...[d, e, f] ] -> [a, b, c, d, e, f]
|
||||||
|
ctx.diagnostic_with_fix(diagnostic, |fixer| {
|
||||||
|
let mut codegen = fixer.codegen();
|
||||||
|
codegen.print(b'[');
|
||||||
|
let elements =
|
||||||
|
spreads.iter().flat_map(|arr| arr.elements.iter()).collect::<Vec<_>>();
|
||||||
|
let n = elements.len();
|
||||||
|
for (i, el) in elements.into_iter().enumerate() {
|
||||||
|
codegen.print_expression(el.to_expression());
|
||||||
|
if i < n - 1 {
|
||||||
|
codegen.print(b',');
|
||||||
|
codegen.print_hard_space();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// foo(...[ ])
|
codegen.print(b']');
|
||||||
AstKind::Argument(_) => {
|
fixer.replace(outer_array.span, codegen)
|
||||||
ctx.diagnostic_with_fix(spread_in_arguments(span), |fixer| {
|
});
|
||||||
fix_by_removing_spread(fixer, array_expr, spread_elem)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -608,6 +662,8 @@ fn test() {
|
||||||
|
|
||||||
let fix = vec![
|
let fix = vec![
|
||||||
("[...[1,2,3]]", "[1,2,3]"),
|
("[...[1,2,3]]", "[1,2,3]"),
|
||||||
|
("[...[1,2,3], ...[4,5,6]]", "[1, 2, 3, 4, 5, 6]"),
|
||||||
|
("[...[1,2,3], ...x]", "[...[1,2,3], ...x]"),
|
||||||
("[...[...[1,2,3]]]", "[...[1,2,3]]"),
|
("[...[...[1,2,3]]]", "[...[1,2,3]]"),
|
||||||
("[...foo.map(x => !!x)]", "foo.map(x => !!x)"),
|
("[...foo.map(x => !!x)]", "foo.map(x => !!x)"),
|
||||||
(r"[...await Promise.all(foo)]", r"await Promise.all(foo)"),
|
(r"[...await Promise.all(foo)]", r"await Promise.all(foo)"),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue