fix(linter/unicorn): handle optional chaining in prefer-array-flat and no-invalid-remove-event-listener (#21299)

fixes commented out tests with optionals in in `unicorn/prefer-array-flat` and `unicorn/no-invalid-remove-event-listener` rules

---------

Co-authored-by: Cameron <cameron.clark@hey.com>
This commit is contained in:
Mikhail BaevandCameron authored and GitHub committed 2026-04-13 09:15:33 +01:00
1 parent 5e5573553a
commit b577efc2ca
3 files changed
+49 -24

No files matched your search

@@ -72,10 +72,6 @@ impl Rule for NoInvalidRemoveEventListener {
_ => return,
};
if member_expr.optional() {
return;
}
if matches!(call_expr.arguments.first(), Some(Argument::SpreadElement(_))) {
return;
}
@@ -133,7 +129,6 @@ fn test() {
let pass = vec![
r#"new el.removeEventListener("click", () => {})"#,
r#"el?.removeEventListener("click", () => {})"#,
r#"el.removeEventListener?.("click", () => {})"#,
r#"el.notRemoveEventListener("click", () => {})"#,
r#"el[removeEventListener]("click", () => {})"#,
@@ -164,9 +159,9 @@ fn test() {
r#"window.removeEventListener("keydown", function () {})"#,
r#"el.removeEventListener("click", (e) => { e.preventDefault(); })"#,
r#"el.removeEventListener("mouseover", fn.bind(abc))"#,
// r#"el?.removeEventListener("mouseover", fn.bind(abc))"#,
r#"el?.removeEventListener("mouseover", fn.bind(abc))"#,
r#"el.removeEventListener("mouseout", function (e) {})"#,
// r#"el?.removeEventListener("mouseout", function (e) {})"#,
r#"el?.removeEventListener("mouseout", function (e) {})"#,
r#"el.removeEventListener("mouseout", function (e) {}, true)"#,
r#"el.removeEventListener("click", function (e) {}, ...moreArguments)"#,
"el.removeEventListener(() => {}, () => {}, () => {})",
@@ -244,16 +244,24 @@ fn check_array_reduce_case<'a>(call_expr: &CallExpression<'a>, ctx: &LintContext
// `[].concat(maybeArray)`
// `[].concat(...array)`
fn check_array_concat_case<'a>(call_expr: &CallExpression<'a>, ctx: &LintContext<'a>) {
if is_method_call(call_expr, None, Some(&["concat"]), Some(1), Some(1)) {
// `array.concat(maybeArray)`
if let Expression::ArrayExpression(array_expr) =
call_expr.callee.get_member_expr().unwrap().object()
{
if !array_expr.elements.is_empty() {
return;
}
ctx.diagnostic(prefer_array_flat_diagnostic(call_expr.span));
if call_expr.optional || !is_method_call(call_expr, None, Some(&["concat"]), Some(1), Some(1)) {
return;
}
let Some(member_expr) = call_expr.callee.as_member_expression() else {
return;
};
if member_expr.optional() {
return;
}
// `array.concat(maybeArray)`
if let Expression::ArrayExpression(array_expr) = member_expr.object() {
if !array_expr.elements.is_empty() {
return;
}
ctx.diagnostic(prefer_array_flat_diagnostic(call_expr.span));
}
}
@@ -265,6 +273,10 @@ fn check_array_prototype_concat_case<'a>(call_expr: &CallExpression<'a>, ctx: &L
return;
};
if call_expr.optional || member_expr.optional() {
return;
}
if let Some(member_expr_obj) = member_expr.object().as_member_expression() {
let is_call_call = is_method_call(call_expr, None, Some(&["call"]), Some(2), Some(2));
@@ -365,8 +377,8 @@ fn test() {
"({}).concat(array)",
"[].concat()",
"[].concat(array, EXTRA_ARGUMENT)",
// "[]?.concat(array)",
// "[].concat?.(array)",
"[]?.concat(array)",
"[].concat?.(array)",
"new [].concat(...array)",
"[][concat](...array)",
"[].notConcat(...array)",
@@ -374,8 +386,8 @@ fn test() {
"({}).concat(...array)",
"[].concat()",
"[].concat(...array, EXTRA_ARGUMENT)",
// "[]?.concat(...array)",
// "[].concat?.(...array)",
"[]?.concat(...array)",
"[].concat?.(...array)",
"new [].concat.apply([], array)",
"[].concat.apply",
"[].concat.apply([], ...array)",
@@ -388,8 +400,8 @@ fn test() {
"[][concat].apply([], array)",
"[].concat.notApply([], array)",
"[].notConcat.apply([], array)",
// "[].concat.apply?.([], array)",
// "[].concat?.apply([], array)",
"[].concat.apply?.([], array)",
"[].concat?.apply([], array)",
"[]?.concat.apply([], array)",
"new Array.prototype.concat.apply([], array)",
"Array.prototype.concat.apply",
@@ -405,8 +417,8 @@ fn test() {
"Array.prototype.notConcat.apply([], array)",
"Array.notPrototype.concat.apply([], array)",
"NotArray.prototype.concat.apply([], array)",
// "Array.prototype.concat.apply?.([], array)",
// "Array.prototype.concat?.apply([], array)",
"Array.prototype.concat.apply?.([], array)",
"Array.prototype.concat?.apply([], array)",
"Array.prototype?.concat.apply([], array)",
"Array?.prototype.concat.apply([], array)",
"object.Array.prototype.concat.apply([], array)",
@@ -56,6 +56,15 @@ source: crates/oxc_linter/src/tester.rs
╰────
help: The listener argument should be a function reference.
⚠ eslint-plugin-unicorn(no-invalid-remove-event-listener): Invalid `removeEventListener` call.
╭─[no_invalid_remove_event_listener.tsx:1:5]
1 │ el?.removeEventListener("mouseover", fn.bind(abc))
· ─────────┬───────── ──────┬─────
· │ ╰── Invalid argument here
· ╰── `removeEventListener` called here.
╰────
help: The listener argument should be a function reference.
⚠ eslint-plugin-unicorn(no-invalid-remove-event-listener): Invalid `removeEventListener` call.
╭─[no_invalid_remove_event_listener.tsx:1:4]
1 │ el.removeEventListener("mouseout", function (e) {})
@@ -65,6 +74,15 @@ source: crates/oxc_linter/src/tester.rs
╰────
help: The listener argument should be a function reference.
⚠ eslint-plugin-unicorn(no-invalid-remove-event-listener): Invalid `removeEventListener` call.
╭─[no_invalid_remove_event_listener.tsx:1:5]
1 │ el?.removeEventListener("mouseout", function (e) {})
· ─────────┬───────── ───────┬───────
· │ ╰── Invalid argument here
· ╰── `removeEventListener` called here.
╰────
help: The listener argument should be a function reference.
⚠ eslint-plugin-unicorn(no-invalid-remove-event-listener): Invalid `removeEventListener` call.
╭─[no_invalid_remove_event_listener.tsx:1:4]
1 │ el.removeEventListener("mouseout", function (e) {}, true)