feat(linter) oxc: approx constant (#1818)

Co-authored-by: Dunqing <dengqing0821@gmail.com>
This commit is contained in:
Cameron 2023-12-26 04:19:44 +00:00 committed by GitHub
parent ce851bb9a5
commit 7d9d920148
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 166 additions and 0 deletions

View file

@ -246,6 +246,7 @@ mod jsx_a11y {
}
mod oxc {
pub mod approx_constant;
pub mod const_comparisons;
pub mod double_comparisons;
pub mod no_accumulating_spread;
@ -467,6 +468,7 @@ oxc_macros::declare_all_lint_rules! {
jsx_a11y::scope,
jsx_a11y::tab_index_no_positive,
jsx_a11y::no_distracting_elements,
oxc::approx_constant,
oxc::const_comparisons,
oxc::double_comparisons,
oxc::no_accumulating_spread,

View file

@ -0,0 +1,96 @@
// Based on https://github.com/rust-lang/rust-clippy//blob/c9a43b18f11219fa70fe632b29518581fcd589c8/clippy_lints/src/approx_const.rs
// https://rust-lang.github.io/rust-clippy/master/#approx_constant
use oxc_ast::AstKind;
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::{self, Error},
};
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use std::f64::consts as f64;
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("oxc(approx-constant): Approximate value of `{1}` found.")]
#[diagnostic(severity(warning), help("Use `Math.{1}` instead"))]
struct ApproxConstantDiagnostic(#[label] pub Span, pub &'static str);
#[derive(Debug, Default, Clone)]
pub struct ApproxConstant;
declare_oxc_lint!(
/// ### What it does
///
/// Disallows the use of approximate constants, instead preferring the use of the constants in the `Math` object.
///
/// ### Why is this bad?
///
/// Approximate constants are not as accurate as the constants in the `Math` object.
///
/// ### Example
/// ```javascript
/// ```
ApproxConstant,
suspicious
);
impl Rule for ApproxConstant {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::NumberLiteral(number_literal) = node.kind() else {
return;
};
let number_lit_str = number_literal.value.to_string();
for (constant, name, min_digits) in &KNOWN_CONSTS {
if is_approx_const(*constant, &number_lit_str, *min_digits) {
ctx.diagnostic(ApproxConstantDiagnostic(number_literal.span, name));
}
}
}
}
const KNOWN_CONSTS: [(f64, &str, usize); 8] = [
(f64::E, "E", 4),
(f64::LN_10, "LN10", 4),
(f64::LN_2, "LN2", 4),
(f64::LOG2_E, "LOG2E", 4),
(f64::LOG10_E, "LOG10E", 4),
(f64::PI, "PI", 4),
(f64::FRAC_1_SQRT_2, "SQRT1_2", 4),
(f64::SQRT_2, "SQRT2", 4),
];
#[must_use]
fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
if value.len() <= min_digits {
false
} else if constant.to_string().starts_with(value) {
// The value is a truncated constant
true
} else {
let round_const = format!("{constant:.*}", value.len() - 2);
value == round_const
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec!["const x = 1234;"];
let fail = vec![
"const getArea = (radius) => 3.141 * radius * radius;",
"let e = 2.718281", // E
"let ln10 = 2.302585", // LN10
"let ln2 = 0.693147", // LN2
"let log10e = 0.434294", // LOG10E
"let log2e = 1.442695", // LOG2E
"let pi = 3.141592", // PI
"let sqrt12 = 0.707106", // SQRT1_2
"let sqrt2 = 1.414213", // SQRT2
];
Tester::new_without_config(ApproxConstant::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,68 @@
---
source: crates/oxc_linter/src/tester.rs
expression: approx_constant
---
⚠ oxc(approx-constant): Approximate value of `PI` found.
╭─[approx_constant.tsx:1:1]
1 │ const getArea = (radius) => 3.141 * radius * radius;
· ─────
╰────
help: Use `Math.PI` instead
⚠ oxc(approx-constant): Approximate value of `E` found.
╭─[approx_constant.tsx:1:1]
1 │ let e = 2.718281
· ────────
╰────
help: Use `Math.E` instead
⚠ oxc(approx-constant): Approximate value of `LN10` found.
╭─[approx_constant.tsx:1:1]
1 │ let ln10 = 2.302585
· ────────
╰────
help: Use `Math.LN10` instead
⚠ oxc(approx-constant): Approximate value of `LN2` found.
╭─[approx_constant.tsx:1:1]
1 │ let ln2 = 0.693147
· ────────
╰────
help: Use `Math.LN2` instead
⚠ oxc(approx-constant): Approximate value of `LOG10E` found.
╭─[approx_constant.tsx:1:1]
1 │ let log10e = 0.434294
· ────────
╰────
help: Use `Math.LOG10E` instead
⚠ oxc(approx-constant): Approximate value of `LOG2E` found.
╭─[approx_constant.tsx:1:1]
1 │ let log2e = 1.442695
· ────────
╰────
help: Use `Math.LOG2E` instead
⚠ oxc(approx-constant): Approximate value of `PI` found.
╭─[approx_constant.tsx:1:1]
1 │ let pi = 3.141592
· ────────
╰────
help: Use `Math.PI` instead
⚠ oxc(approx-constant): Approximate value of `SQRT1_2` found.
╭─[approx_constant.tsx:1:1]
1 │ let sqrt12 = 0.707106
· ────────
╰────
help: Use `Math.SQRT1_2` instead
⚠ oxc(approx-constant): Approximate value of `SQRT2` found.
╭─[approx_constant.tsx:1:1]
1 │ let sqrt2 = 1.414213
· ────────
╰────
help: Use `Math.SQRT2` instead