mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(linter/eslint): Implement prefer-exponentiation-operator (#3365)
Rule Detail: [link](https://eslint.org/docs/latest/rules/prefer-exponentiation-operator)
This commit is contained in:
parent
283d6c7bb4
commit
c588e5205c
3 changed files with 210 additions and 0 deletions
|
|
@ -101,6 +101,7 @@ mod eslint {
|
|||
pub mod no_var;
|
||||
pub mod no_void;
|
||||
pub mod no_with;
|
||||
pub mod prefer_exponentiation_operator;
|
||||
pub mod radix;
|
||||
pub mod require_yield;
|
||||
pub mod symbol_description;
|
||||
|
|
@ -470,6 +471,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
eslint::valid_typeof,
|
||||
eslint::no_await_in_loop,
|
||||
eslint::no_new_native_nonconstructor,
|
||||
eslint::prefer_exponentiation_operator,
|
||||
typescript::adjacent_overload_signatures,
|
||||
typescript::array_type,
|
||||
typescript::ban_ts_comment,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,132 @@
|
|||
use oxc_ast::{ast::Expression, match_member_expression, AstKind};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{
|
||||
ast_util::is_method_call, context::LintContext, globals::GLOBAL_OBJECT_NAMES, rule::Rule,
|
||||
AstNode,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct PreferExponentiationOperator;
|
||||
|
||||
fn prefer_exponentian_operator_diagnostic(span0: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn(
|
||||
"eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.",
|
||||
)
|
||||
.with_labels([span0.into()])
|
||||
}
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
///
|
||||
/// Disallow the use of Math.pow in favor of the ** operator
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Introduced in ES2016, the infix exponentiation operator ** is an alternative for the
|
||||
/// standard Math.pow function. Infix notation is considered to be more readable and thus more
|
||||
/// preferable than the function notation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```javascript
|
||||
/// Math.pow(a, b)
|
||||
/// ```
|
||||
PreferExponentiationOperator,
|
||||
style,
|
||||
);
|
||||
|
||||
impl Rule for PreferExponentiationOperator {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
let AstKind::CallExpression(call_expr) = node.kind() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !is_method_call(call_expr, None, Some(&["pow"]), Some(2), Some(2)) {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(member_expr) = call_expr.callee.as_member_expression() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let member_expor_obj = member_expr.object();
|
||||
|
||||
match member_expor_obj {
|
||||
Expression::Identifier(ident) => {
|
||||
if ident.name.as_str() == "Math"
|
||||
&& ctx.semantic().is_reference_to_global_variable(ident)
|
||||
{
|
||||
ctx.diagnostic(prefer_exponentian_operator_diagnostic(call_expr.span));
|
||||
}
|
||||
}
|
||||
match_member_expression!(Expression) => {
|
||||
let member_expr = member_expor_obj.to_member_expression();
|
||||
let Some(static_prop_name) = member_expr.static_property_name() else {
|
||||
return;
|
||||
};
|
||||
if static_prop_name != "Math" {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Expression::Identifier(ident) = member_expr.object().without_parenthesized()
|
||||
{
|
||||
if GLOBAL_OBJECT_NAMES.contains(ident.name.as_str())
|
||||
&& ctx.semantic().is_reference_to_global_variable(ident)
|
||||
{
|
||||
ctx.diagnostic(prefer_exponentian_operator_diagnostic(call_expr.span));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec",
|
||||
"globalThis.Object.pow(a, b)",
|
||||
"globalThis.Math.max(a, b)",
|
||||
// "/* globals Math:off*/ Math.pow(a, b)",
|
||||
"let Math; Math.pow(a, b);",
|
||||
"if (foo) { const Math = 1; Math.pow(a, b); }",
|
||||
"var x = function Math() { Math.pow(a, b); }",
|
||||
"function foo(Math) { Math.pow(a, b); }",
|
||||
"function foo() { Math.pow(a, b); var Math; }",
|
||||
"
|
||||
var globalThis = bar;
|
||||
globalThis.Math.pow(a, b)
|
||||
",
|
||||
"class C { #pow; foo() { Math.#pow(a, b); } }",
|
||||
];
|
||||
|
||||
let fail = vec",
|
||||
"Math.pow(a, b) + Math.pow(c,
|
||||
d)",
|
||||
"Math.pow(Math.pow(a, b), Math.pow(c, d))",
|
||||
"Math.pow(a, b)**Math.pow(c, d)",
|
||||
"Math.pow(a, b as any)",
|
||||
"Math.pow(a as any, b)",
|
||||
"Math.pow(a, b) as any",
|
||||
];
|
||||
|
||||
Tester::new(PreferExponentiationOperator::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
expression: prefer_exponentiation_operator
|
||||
---
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:1]
|
||||
1 │ globalThis.Math.pow(a, b)
|
||||
· ─────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:1]
|
||||
1 │ globalThis.Math['pow'](a, b)
|
||||
· ────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:1]
|
||||
1 │ Math.pow(a, b) + Math.pow(c,
|
||||
· ──────────────
|
||||
2 │ d)
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:18]
|
||||
1 │ ╭─▶ Math.pow(a, b) + Math.pow(c,
|
||||
2 │ ╰─▶ d)
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:1]
|
||||
1 │ Math.pow(Math.pow(a, b), Math.pow(c, d))
|
||||
· ────────────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:10]
|
||||
1 │ Math.pow(Math.pow(a, b), Math.pow(c, d))
|
||||
· ──────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:26]
|
||||
1 │ Math.pow(Math.pow(a, b), Math.pow(c, d))
|
||||
· ──────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:1]
|
||||
1 │ Math.pow(a, b)**Math.pow(c, d)
|
||||
· ──────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:17]
|
||||
1 │ Math.pow(a, b)**Math.pow(c, d)
|
||||
· ──────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:1]
|
||||
1 │ Math.pow(a, b as any)
|
||||
· ─────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:1]
|
||||
1 │ Math.pow(a as any, b)
|
||||
· ─────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-exponentian-operator): Prefer `**` over `Math.pow`.
|
||||
╭─[prefer_exponentiation_operator.tsx:1:1]
|
||||
1 │ Math.pow(a, b) as any
|
||||
· ──────────────
|
||||
╰────
|
||||
Loading…
Reference in a new issue