diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs b/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs index cfc04a4db23ee..f847d7a8677c5 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs @@ -73,7 +73,7 @@ declare_oxc_lint!( PreferNumberProperties, unicorn, restriction, - pending + fix ); impl Rule for PreferNumberProperties { @@ -100,40 +100,45 @@ impl Rule for PreferNumberProperties { }; if GLOBAL_OBJECT_NAMES.contains(&ident_name.name.as_str()) { - match member_expr.static_property_name() { - Some("NaN") if self.check_nan => { - ctx.diagnostic(prefer_number_properties_diagnostic( - member_expr.span(), - "NaN", - )); - } - Some("Infinity") if self.check_infinity => { - ctx.diagnostic(prefer_number_properties_diagnostic( - member_expr.span(), - "Infinity", - )); - } - _ => {} + let Some(name) = member_expr.static_property_name() else { return }; + if (name == "NaN" && self.check_nan) + || (name == "Infinity" && self.check_infinity) + { + ctx.diagnostic_with_fix( + prefer_number_properties_diagnostic(member_expr.span(), name), + |fixer| fixer.replace(ident_name.span, "Number"), + ); } } } AstKind::IdentifierReference(ident_ref) if ctx.is_reference_to_global_variable(ident_ref) => { - match ident_ref.name.as_str() { - "NaN" if self.check_nan => { - ctx.diagnostic(prefer_number_properties_diagnostic( - ident_ref.span, - &ident_ref.name, - )); - } - "Infinity" if self.check_infinity => { - ctx.diagnostic(prefer_number_properties_diagnostic( - ident_ref.span, - &ident_ref.name, - )); - } - _ => {} + if (ident_ref.name.as_str() == "NaN" && self.check_nan) + || (ident_ref.name.as_str() == "Infinity" && self.check_infinity) + || (matches!( + ident_ref.name.as_str(), + "isNaN" | "isFinite" | "parseFloat" | "parseInt" + ) && matches!( + ctx.nodes().parent_kind(node.id()), + Some(AstKind::ObjectProperty(_)) + )) + { + ctx.diagnostic_with_fix( + prefer_number_properties_diagnostic(ident_ref.span, &ident_ref.name), + |fixer| match ctx.nodes().parent_kind(node.id()) { + Some(AstKind::ObjectProperty(object_property)) + if object_property.shorthand => + { + fixer.insert_text_before( + &ident_ref.span, + format!("{}: Number.", ident_ref.name.as_str()), + ) + } + Some(_) => fixer.insert_text_before(&ident_ref.span, "Number."), + None => unreachable!(), + }, + ); } } AstKind::CallExpression(call_expr) => { @@ -148,10 +153,20 @@ impl Rule for PreferNumberProperties { } } - ctx.diagnostic(prefer_number_properties_diagnostic( - call_expr.callee.span(), - ident_name, - )); + ctx.diagnostic_with_fix( + prefer_number_properties_diagnostic(call_expr.callee.span(), ident_name), + |fixer| match &call_expr.callee { + Expression::Identifier(ident) => { + fixer.insert_text_before(&ident.span, "Number.") + } + match_member_expression!(Expression) => { + let member_expr = call_expr.callee.to_member_expression(); + + fixer.replace(member_expr.object().span(), "Number") + } + _ => unreachable!(), + }, + ); } } _ => {} @@ -402,18 +417,18 @@ function inner() { (r"self.parseFloat(foo);", None), (r"globalThis.NaN", None), (r"-globalThis.Infinity", Some(json!([{"checkInfinity":true}]))), - // ( - // r"const options = { - // normalize: parseFloat, - // parseInt, - // }; + ( + r"const options = { + normalize: parseFloat, + parseInt, + }; - // run(foo, options);", - // None, - // ), + run(foo, options);", + None, + ), ]; - let _fix = vec![ + let fix = vec![ ( r#"const a = parseInt("10", 2); const b = parseFloat("10.5"); @@ -421,8 +436,8 @@ function inner() { const d = isFinite(10);"#, r#"const a = Number.parseInt("10", 2); const b = Number.parseFloat("10.5"); - const c = isNaN(10); - const d = isFinite(10);"#, + const c = Number.isNaN(10); + const d = Number.isFinite(10);"#, None::, ), ("const foo = NaN;", "const foo = Number.NaN;", None), @@ -439,5 +454,6 @@ function inner() { ]; Tester::new(PreferNumberProperties::NAME, PreferNumberProperties::PLUGIN, pass, fail) + .expect_fix(fix) .test_and_snapshot(); } diff --git a/crates/oxc_linter/src/snapshots/unicorn_prefer_number_properties.snap b/crates/oxc_linter/src/snapshots/unicorn_prefer_number_properties.snap index 90cfda9e091df..0d90794321aa6 100644 --- a/crates/oxc_linter/src/snapshots/unicorn_prefer_number_properties.snap +++ b/crates/oxc_linter/src/snapshots/unicorn_prefer_number_properties.snap @@ -489,3 +489,21 @@ source: crates/oxc_linter/src/tester.rs · ─────────────────── ╰──── help: Replace it with `Number.Infinity` + + ⚠ eslint-plugin-unicorn(prefer-number-properties): Use `Number.parseFloat` instead of the global `parseFloat` + ╭─[prefer_number_properties.tsx:2:24] + 1 │ const options = { + 2 │ normalize: parseFloat, + · ────────── + 3 │ parseInt, + ╰──── + help: Replace it with `Number.parseFloat` + + ⚠ eslint-plugin-unicorn(prefer-number-properties): Use `Number.parseInt` instead of the global `parseInt` + ╭─[prefer_number_properties.tsx:3:14] + 2 │ normalize: parseFloat, + 3 │ parseInt, + · ──────── + 4 │ }; + ╰──── + help: Replace it with `Number.parseInt`