diff --git a/crates/oxc_linter/src/rules/unicorn/require_array_join_separator.rs b/crates/oxc_linter/src/rules/unicorn/require_array_join_separator.rs index eb380149b6817..2ec6a800155b6 100644 --- a/crates/oxc_linter/src/rules/unicorn/require_array_join_separator.rs +++ b/crates/oxc_linter/src/rules/unicorn/require_array_join_separator.rs @@ -37,7 +37,7 @@ declare_oxc_lint!( /// ``` RequireArrayJoinSeparator, style, - pending + conditional_fix ); fn is_array_prototype_property(member_expr: &MemberExpression, property: &str) -> bool { @@ -59,10 +59,36 @@ impl Rule for RequireArrayJoinSeparator { && !call_expr.optional && !matches!(member_expr, MemberExpression::ComputedMemberExpression(_)) { - ctx.diagnostic(require_array_join_separator_diagnostic(Span::new( - member_expr.span().end, - call_expr.span.end, - ))); + ctx.diagnostic_with_fix( + require_array_join_separator_diagnostic(Span::new( + member_expr.span().end, + call_expr.span.end, + )), + |fixer| { + // after end of `join`, find the `(` and insert `","` + let open_bracket = ctx + .source_range(call_expr.span) + .chars() + .skip(member_expr.span().size() as usize) + .position(|c| c == '('); + + if let Some(open_bracket) = open_bracket { + #[allow(clippy::cast_possible_truncation)] + fixer.insert_text_after_range( + Span::new( + 0, + call_expr.span.start + + member_expr.span().size() + + open_bracket as u32 + + 1, + ), + r#"",""#, + ) + } else { + fixer.noop() + } + }, + ); } // `[].join.call(foo)` and `Array.prototype.join.call(foo)` @@ -73,10 +99,20 @@ impl Rule for RequireArrayJoinSeparator { && !call_expr.arguments.iter().any(oxc_ast::ast::Argument::is_spread) && is_array_prototype_property(member_expr_obj, "join") { - ctx.diagnostic(require_array_join_separator_diagnostic(Span::new( - member_expr.span().end, - call_expr.span.end, - ))); + ctx.diagnostic_with_fix( + require_array_join_separator_diagnostic(Span::new( + member_expr.span().end, + call_expr.span.end, + )), + |fixer| { + // after the end of the first argument, insert `","` + let first_arg = call_expr.arguments.first().unwrap(); + fixer.insert_text_after_range( + Span::new(first_arg.span().end, first_arg.span().end), + r#", ",""#, + ) + }, + ); } } } @@ -87,14 +123,14 @@ fn test() { use crate::tester::Tester; let pass = vec![ - ("foo.join(\",\")", None), + (r#"foo.join(",")"#, None), (r"join()", None), (r"foo.join(...[])", None), (r"foo.join?.()", None), (r"foo?.join?.()", None), (r"foo[join]()", None), - ("foo[\"join\"]()", None), - ("[].join.call(foo, \",\")", None), + (r#"foo["join"]()"#, None), + (r#"[].join.call(foo, ",")"#, None), (r"[].join.call()", None), (r"[].join.call(...[foo])", None), (r"[].join?.call(foo)", None), @@ -104,20 +140,20 @@ fn test() { (r"[,].join.call(foo)", None), (r"[].join.notCall(foo)", None), (r"[].notJoin.call(foo)", None), - ("Array.prototype.join.call(foo, \"\")", None), + (r#"Array.prototype.join.call(foo, "")"#, None), (r"Array.prototype.join.call()", None), (r"Array.prototype.join.call(...[foo])", None), (r"Array.prototype.join?.call(foo)", None), (r"Array.prototype?.join.call(foo)", None), (r"Array?.prototype.join.call(foo)", None), - ("Array.prototype.join[call](foo, \"\")", None), + (r#"Array.prototype.join[call](foo, "")"#, None), (r"Array.prototype[join].call(foo)", None), (r"Array[prototype].join.call(foo)", None), (r"Array.prototype.join.notCall(foo)", None), (r"Array.prototype.notJoin.call(foo)", None), (r"Array.notPrototype.join.call(foo)", None), (r"NotArray.prototype.join.call(foo)", None), - ("path.join(__dirname, \"./foo.js\")", None), + (r#"path.join(__dirname, "./foo.js")"#, None), ]; let fail = vec![ @@ -130,5 +166,16 @@ fn test() { (r"foo?.join()", None), ]; - Tester::new(RequireArrayJoinSeparator::NAME, pass, fail).test_and_snapshot(); + let fix = vec![ + (r"foo.join()", r#"foo.join(",")"#), + (r"foo.join ()", r#"foo.join (",")"#), + (r"[].join.call(foo)", r#"[].join.call(foo, ",")"#), + (r"[].join.call(foo,)", r#"[].join.call(foo, ",",)"#), + (r"[].join.call(foo , );", r#"[].join.call(foo, "," , );"#), + (r"Array.prototype.join.call(foo)", r#"Array.prototype.join.call(foo, ",")"#), + (r"Array.prototype.join.call(foo, )", r#"Array.prototype.join.call(foo, ",", )"#), + (r"foo?.join()", r#"foo?.join(",")"#), + ]; + + Tester::new(RequireArrayJoinSeparator::NAME, pass, fail).expect_fix(fix).test_and_snapshot(); }