Skip to content

Commit a3ada34

Browse files
committed
feat(linter): implement fixer for unicorn/prefer-number-properties (#10693)
part of #10477
1 parent c066be1 commit a3ada34

File tree

2 files changed

+78
-44
lines changed

2 files changed

+78
-44
lines changed

crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs

Lines changed: 60 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ declare_oxc_lint!(
7373
PreferNumberProperties,
7474
unicorn,
7575
restriction,
76-
pending
76+
fix
7777
);
7878

7979
impl Rule for PreferNumberProperties {
@@ -100,40 +100,45 @@ impl Rule for PreferNumberProperties {
100100
};
101101

102102
if GLOBAL_OBJECT_NAMES.contains(&ident_name.name.as_str()) {
103-
match member_expr.static_property_name() {
104-
Some("NaN") if self.check_nan => {
105-
ctx.diagnostic(prefer_number_properties_diagnostic(
106-
member_expr.span(),
107-
"NaN",
108-
));
109-
}
110-
Some("Infinity") if self.check_infinity => {
111-
ctx.diagnostic(prefer_number_properties_diagnostic(
112-
member_expr.span(),
113-
"Infinity",
114-
));
115-
}
116-
_ => {}
103+
let Some(name) = member_expr.static_property_name() else { return };
104+
if (name == "NaN" && self.check_nan)
105+
|| (name == "Infinity" && self.check_infinity)
106+
{
107+
ctx.diagnostic_with_fix(
108+
prefer_number_properties_diagnostic(member_expr.span(), name),
109+
|fixer| fixer.replace(ident_name.span, "Number"),
110+
);
117111
}
118112
}
119113
}
120114
AstKind::IdentifierReference(ident_ref)
121115
if ctx.is_reference_to_global_variable(ident_ref) =>
122116
{
123-
match ident_ref.name.as_str() {
124-
"NaN" if self.check_nan => {
125-
ctx.diagnostic(prefer_number_properties_diagnostic(
126-
ident_ref.span,
127-
&ident_ref.name,
128-
));
129-
}
130-
"Infinity" if self.check_infinity => {
131-
ctx.diagnostic(prefer_number_properties_diagnostic(
132-
ident_ref.span,
133-
&ident_ref.name,
134-
));
135-
}
136-
_ => {}
117+
if (ident_ref.name.as_str() == "NaN" && self.check_nan)
118+
|| (ident_ref.name.as_str() == "Infinity" && self.check_infinity)
119+
|| (matches!(
120+
ident_ref.name.as_str(),
121+
"isNaN" | "isFinite" | "parseFloat" | "parseInt"
122+
) && matches!(
123+
ctx.nodes().parent_kind(node.id()),
124+
Some(AstKind::ObjectProperty(_))
125+
))
126+
{
127+
ctx.diagnostic_with_fix(
128+
prefer_number_properties_diagnostic(ident_ref.span, &ident_ref.name),
129+
|fixer| match ctx.nodes().parent_kind(node.id()) {
130+
Some(AstKind::ObjectProperty(object_property))
131+
if object_property.shorthand =>
132+
{
133+
fixer.insert_text_before(
134+
&ident_ref.span,
135+
format!("{}: Number.", ident_ref.name.as_str()),
136+
)
137+
}
138+
Some(_) => fixer.insert_text_before(&ident_ref.span, "Number."),
139+
None => unreachable!(),
140+
},
141+
);
137142
}
138143
}
139144
AstKind::CallExpression(call_expr) => {
@@ -148,10 +153,20 @@ impl Rule for PreferNumberProperties {
148153
}
149154
}
150155

151-
ctx.diagnostic(prefer_number_properties_diagnostic(
152-
call_expr.callee.span(),
153-
ident_name,
154-
));
156+
ctx.diagnostic_with_fix(
157+
prefer_number_properties_diagnostic(call_expr.callee.span(), ident_name),
158+
|fixer| match &call_expr.callee {
159+
Expression::Identifier(ident) => {
160+
fixer.insert_text_before(&ident.span, "Number.")
161+
}
162+
match_member_expression!(Expression) => {
163+
let member_expr = call_expr.callee.to_member_expression();
164+
165+
fixer.replace(member_expr.object().span(), "Number")
166+
}
167+
_ => unreachable!(),
168+
},
169+
);
155170
}
156171
}
157172
_ => {}
@@ -402,27 +417,27 @@ function inner() {
402417
(r"self.parseFloat(foo);", None),
403418
(r"globalThis.NaN", None),
404419
(r"-globalThis.Infinity", Some(json!([{"checkInfinity":true}]))),
405-
// (
406-
// r"const options = {
407-
// normalize: parseFloat,
408-
// parseInt,
409-
// };
420+
(
421+
r"const options = {
422+
normalize: parseFloat,
423+
parseInt,
424+
};
410425
411-
// run(foo, options);",
412-
// None,
413-
// ),
426+
run(foo, options);",
427+
None,
428+
),
414429
];
415430

416-
let _fix = vec![
431+
let fix = vec![
417432
(
418433
r#"const a = parseInt("10", 2);
419434
const b = parseFloat("10.5");
420435
const c = isNaN(10);
421436
const d = isFinite(10);"#,
422437
r#"const a = Number.parseInt("10", 2);
423438
const b = Number.parseFloat("10.5");
424-
const c = isNaN(10);
425-
const d = isFinite(10);"#,
439+
const c = Number.isNaN(10);
440+
const d = Number.isFinite(10);"#,
426441
None::<Value>,
427442
),
428443
("const foo = NaN;", "const foo = Number.NaN;", None),
@@ -439,5 +454,6 @@ function inner() {
439454
];
440455

441456
Tester::new(PreferNumberProperties::NAME, PreferNumberProperties::PLUGIN, pass, fail)
457+
.expect_fix(fix)
442458
.test_and_snapshot();
443459
}

crates/oxc_linter/src/snapshots/unicorn_prefer_number_properties.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,3 +489,21 @@ source: crates/oxc_linter/src/tester.rs
489489
· ───────────────────
490490
╰────
491491
help: Replace it with `Number.Infinity`
492+
493+
eslint-plugin-unicorn(prefer-number-properties): Use `Number.parseFloat` instead of the global `parseFloat`
494+
╭─[prefer_number_properties.tsx:2:24]
495+
1const options = {
496+
2 │ normalize: parseFloat,
497+
· ──────────
498+
3 │ parseInt,
499+
╰────
500+
help: Replace it with `Number.parseFloat`
501+
502+
eslint-plugin-unicorn(prefer-number-properties): Use `Number.parseInt` instead of the global `parseInt`
503+
╭─[prefer_number_properties.tsx:3:14]
504+
2normalize: parseFloat,
505+
3 │ parseInt,
506+
· ────────
507+
4 │ };
508+
╰────
509+
help: Replace it with `Number.parseInt`

0 commit comments

Comments
 (0)