feat(linter): implement number_arg_out_of_range from deepscan

This commit is contained in:
Boshen 2023-06-21 10:42:53 +08:00
parent 0873733ae7
commit 66d1c7b1de
4 changed files with 143 additions and 2 deletions

View file

@ -440,7 +440,7 @@ impl<'a> MemberExpression<'a> {
}
}
pub fn static_property_name(&'a self) -> Option<&'a str> {
pub fn static_property_name(&self) -> Option<&str> {
match self {
MemberExpression::ComputedMemberExpression(expr) => match &expr.expression {
Expression::StringLiteral(lit) => Some(&lit.value),
@ -453,7 +453,7 @@ impl<'a> MemberExpression<'a> {
}
_ => None,
},
MemberExpression::StaticMemberExpression(expr) => Some(&expr.property.name),
MemberExpression::StaticMemberExpression(expr) => Some(expr.property.name.as_str()),
MemberExpression::PrivateFieldExpression(_) => None,
}
}

View file

@ -34,6 +34,7 @@ oxc_macros::declare_all_lint_rules! {
deepscan::missing_throw,
deepscan::bad_min_max_func,
deepscan::bad_remove_event_listener,
deepscan::number_arg_out_of_range,
use_isnan,
valid_typeof,
typescript::isolated_declaration

View file

@ -0,0 +1,93 @@
use oxc_ast::{
ast::{Argument, Expression},
AstKind,
};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::{Atom, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("Radix or precision arguments of number-related functions should not exceed the limit")]
#[diagnostic(
severity(warning),
help("The first argument of 'Number.prototype.{0}' should be a number between {1} and {2}")
)]
struct NumberArgOutOfRangeDiagnostic(Atom, usize, usize, #[label] pub Span);
/// `https://deepscan.io/docs/rules/missing-throw`
#[derive(Debug, Default, Clone)]
pub struct NumberArgOutOfRange;
declare_oxc_lint!(
/// ### What it does
///
/// Checks whether the radix or precision arguments of number-related functions exceeds the limit.
///
/// ### Example
/// ```javascript
/// function foo() { throw Error() }
/// const foo = () => { new Error() }
/// ```
NumberArgOutOfRange,
correctness
);
impl Rule for NumberArgOutOfRange {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::CallExpression(expr) = node.kind() else {
return ;
};
if let Some(member) = expr.callee.get_member_expr() {
if let Some(Argument::Expression(Expression::NumberLiteral(literal))) =
expr.arguments.first()
{
let value = literal.value;
match member.static_property_name() {
Some(name @ "toString") => {
if !(2.0_f64..=36.0_f64).contains(&value) {
let name = Atom::from(name);
ctx.diagnostic(NumberArgOutOfRangeDiagnostic(name, 2, 36, expr.span));
}
}
Some(name @ ("toFixed" | "toExponential")) => {
if !(0.0_f64..=20.0_f64).contains(&value) {
let name = Atom::from(name);
ctx.diagnostic(NumberArgOutOfRangeDiagnostic(name, 0, 20, expr.span));
}
}
Some(name @ "toPrecision") => {
if !(1.0_f64..=21.0_f64).contains(&value) {
let name = Atom::from(name);
ctx.diagnostic(NumberArgOutOfRangeDiagnostic(name, 1, 21, expr.span));
}
}
_ => {}
}
}
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![("var x = 42;var s = x.toString(16);", None)];
let fail = vec![
("var x = 42;var s = x.toString(1);", None),
("var x = 42;var s = x.toString(43);", None),
("var x = 42;var s = x.toFixed(22);", None),
("var x = 42;var s = x['toExponential'](22);", None),
("var x = 42;var s = x.toPrecision(0);", None),
("var x = 42;var s = x.toPrecision(100);", None),
];
Tester::new(NumberArgOutOfRange::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,47 @@
---
source: crates/oxc_linter/src/tester.rs
expression: number_arg_out_of_range
---
⚠ Radix or precision arguments of number-related functions should not exceed the limit
╭─[number_arg_out_of_range.tsx:1:1]
1 │ var x = 42;var s = x.toString(1);
· ─────────────
╰────
help: The first argument of 'Number.prototype.toString' should be a number between 2 and 36
⚠ Radix or precision arguments of number-related functions should not exceed the limit
╭─[number_arg_out_of_range.tsx:1:1]
1 │ var x = 42;var s = x.toString(43);
· ──────────────
╰────
help: The first argument of 'Number.prototype.toString' should be a number between 2 and 36
⚠ Radix or precision arguments of number-related functions should not exceed the limit
╭─[number_arg_out_of_range.tsx:1:1]
1 │ var x = 42;var s = x.toFixed(22);
· ─────────────
╰────
help: The first argument of 'Number.prototype.toFixed' should be a number between 0 and 20
⚠ Radix or precision arguments of number-related functions should not exceed the limit
╭─[number_arg_out_of_range.tsx:1:1]
1 │ var x = 42;var s = x['toExponential'](22);
· ──────────────────────
╰────
help: The first argument of 'Number.prototype.toExponential' should be a number between 0 and 20
⚠ Radix or precision arguments of number-related functions should not exceed the limit
╭─[number_arg_out_of_range.tsx:1:1]
1 │ var x = 42;var s = x.toPrecision(0);
· ────────────────
╰────
help: The first argument of 'Number.prototype.toPrecision' should be a number between 1 and 21
⚠ Radix or precision arguments of number-related functions should not exceed the limit
╭─[number_arg_out_of_range.tsx:1:1]
1 │ var x = 42;var s = x.toPrecision(100);
· ──────────────────
╰────
help: The first argument of 'Number.prototype.toPrecision' should be a number between 1 and 21