diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 959917a90..67cad3c52 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -8,6 +8,7 @@ oxc_macros::declare_all_lint_rules! { for_direction, no_debugger, no_array_constructor, + no_caller, no_empty, no_empty_pattern, no_self_compare, diff --git a/crates/oxc_linter/src/rules/no_caller.rs b/crates/oxc_linter/src/rules/no_caller.rs new file mode 100644 index 000000000..33a95d4dd --- /dev/null +++ b/crates/oxc_linter/src/rules/no_caller.rs @@ -0,0 +1,82 @@ +use oxc_ast::{ast::MemberExpression, AstKind, Span}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint(no-caller): Disallow the use of arguments.caller or arguments.callee")] +#[diagnostic( + severity(warning), + help( + "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them" + ) +)] +struct NoCallerDiagnostic(#[label] pub Span); + +#[derive(Debug, Default, Clone)] +pub struct NoCaller; + +declare_oxc_lint!( + /// ### What it does + /// + /// Disallow the use of arguments.caller or arguments.callee + /// + /// ### Why is this bad? + /// + /// The use of arguments.caller and arguments.callee make several code optimizations impossible. + /// They have been deprecated in future versions of JavaScript and their use is forbidden in ECMAScript 5 while in strict mode. + /// + /// ### Example + /// ```javascript + /// function foo(n) { + /// if (n <= 0) { + /// return; + /// } + /// + /// arguments.callee(n - 1); + /// } + /// + /// [1,2,3,4,5].map(function(n) { + /// return !(n > 1) ? 1 : arguments.callee(n - 1) * n; + /// }); + /// ``` + NoCaller, + correctness +); + +impl Rule for NoCaller { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::MemberExpression(member_expr) = node.get().kind() else {return}; + if let MemberExpression::StaticMemberExpression(expr) = member_expr + && let Some(reference) = expr.object.get_identifier_reference() + { + if reference.name != "arguments" { + return; + } + + if expr.property.name == "callee" || expr.property.name == "caller" { + ctx.diagnostic(NoCallerDiagnostic(expr.property.span)); + } + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ("var x = arguments.length", None), + ("var x = arguments", None), + ("var x = arguments[0]", None), + ("var x = arguments[caller]", None), + ]; + + let fail = vec![("var x = arguments.callee", None), ("var x = arguments.caller", None)]; + + Tester::new(NoCaller::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_caller.snap b/crates/oxc_linter/src/snapshots/no_caller.snap new file mode 100644 index 000000000..90b0a07a2 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_caller.snap @@ -0,0 +1,20 @@ +--- +source: crates/oxc_linter/src/tester.rs +assertion_line: 53 +expression: no_caller +--- + + ⚠ eslint(no-caller): Disallow the use of arguments.caller or arguments.callee + ╭─[no_caller.tsx:1:1] + 1 │ var x = arguments.callee + · ────── + ╰──── + help: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them + + ⚠ eslint(no-caller): Disallow the use of arguments.caller or arguments.callee + ╭─[no_caller.tsx:1:1] + 1 │ var x = arguments.caller + · ────── + ╰──── + help: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them +