mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(linter): implement no-shadow-restricted-names (#277)
This commit is contained in:
parent
ffe12b30bc
commit
161231347b
3 changed files with 306 additions and 0 deletions
|
|
@ -17,6 +17,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
no_empty_pattern,
|
||||
no_new_symbol,
|
||||
no_self_compare,
|
||||
no_shadow_restricted_names,
|
||||
no_mixed_operators,
|
||||
no_constant_binary_expression,
|
||||
no_constant_condition,
|
||||
|
|
|
|||
111
crates/oxc_linter/src/rules/no_shadow_restricted_names.rs
Normal file
111
crates/oxc_linter/src/rules/no_shadow_restricted_names.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
use oxc_ast::{AstKind, Atom, Span};
|
||||
use oxc_diagnostics::{
|
||||
miette::{self, Diagnostic},
|
||||
thiserror::{self, Error},
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::Symbol;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error("eslint(no-shadow-restricted-names): Shadowing of global property '{0}'")]
|
||||
#[diagnostic(severity(warning))]
|
||||
struct NoShadowRestrictedNamesDiagnostic(
|
||||
Atom,
|
||||
#[label("Shadowing of global property '{0}'")] pub Span,
|
||||
);
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NoShadowRestrictedNames;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
/// Disallow identifiers from shadowing restricted names
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// ES5 §15.1.1 Value Properties of the Global Object (NaN, Infinity, undefined) as well as strict mode restricted identifiers eval and arguments are considered to be restricted names in JavaScript.
|
||||
/// Defining them to mean something else can have unintended consequences and confuse others reading the code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```javascript
|
||||
/// var undefined = "foo";
|
||||
/// ```
|
||||
NoShadowRestrictedNames,
|
||||
correctness
|
||||
);
|
||||
|
||||
static RESTRICTED: [&str; 5] = ["undefined", "NaN", "Infinity", "arguments", "eval"];
|
||||
|
||||
fn safely_shadows_undefined(symbol: &Symbol, ctx: &LintContext<'_>) -> bool {
|
||||
if symbol.name().as_str() == "undefined" {
|
||||
let mut no_assign = true;
|
||||
for reference_id in symbol.references() {
|
||||
let reference = ctx.semantic().symbols().get_resolved_reference(*reference_id).unwrap();
|
||||
if reference.is_write() {
|
||||
no_assign = false;
|
||||
}
|
||||
}
|
||||
let mut no_init = false;
|
||||
let decl = ctx.nodes()[symbol.declaration()];
|
||||
if let AstKind::VariableDeclarator(var) = decl.kind() {
|
||||
no_init = var.init.is_none();
|
||||
}
|
||||
return no_assign && no_init;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
impl Rule for NoShadowRestrictedNames {
|
||||
fn run_on_symbol(&self, symbol: &Symbol, ctx: &LintContext<'_>) {
|
||||
if RESTRICTED.contains(&symbol.name().as_str()) && !safely_shadows_undefined(symbol, ctx) {
|
||||
ctx.diagnostic(NoShadowRestrictedNamesDiagnostic(symbol.name().clone(), symbol.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
("function foo(bar){ var baz; }", None),
|
||||
("!function foo(bar){ var baz; }", None),
|
||||
("!function(bar){ var baz; }", None),
|
||||
("try {} catch(e) {}", None),
|
||||
("export default function() {}", None),
|
||||
("var undefined;", None),
|
||||
("var undefined; doSomething(undefined);", None),
|
||||
("var undefined; var undefined;", None),
|
||||
("let undefined", None),
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
("function NaN(NaN) { var NaN; !function NaN(NaN) { try {} catch(NaN) {} }; }", None),
|
||||
(
|
||||
"function undefined(undefined) { !function undefined(undefined) { try {} catch(undefined) {} }; }",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"function Infinity(Infinity) { var Infinity; !function Infinity(Infinity) { try {} catch(Infinity) {} }; }",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"function arguments(arguments) { var arguments; !function arguments(arguments) { try {} catch(arguments) {} }; }",
|
||||
None,
|
||||
),
|
||||
("function eval(eval) { var eval; !function eval(eval) { try {} catch(eval) {} }; }", None),
|
||||
(
|
||||
"var eval = (eval) => { var eval; !function eval(eval) { try {} catch(eval) {} }; }",
|
||||
None,
|
||||
),
|
||||
("var [undefined] = [1]", None),
|
||||
(
|
||||
"var {undefined} = obj; var {a: undefined} = obj; var {a: {b: {undefined}}} = obj; var {a, ...undefined} = obj;",
|
||||
None,
|
||||
),
|
||||
("var undefined; undefined = 5;", None),
|
||||
];
|
||||
|
||||
Tester::new(NoShadowRestrictedNames::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
194
crates/oxc_linter/src/snapshots/no_shadow_restricted_names.snap
Normal file
194
crates/oxc_linter/src/snapshots/no_shadow_restricted_names.snap
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
expression: no_shadow_restricted_names
|
||||
---
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'NaN'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function NaN(NaN) { var NaN; !function NaN(NaN) { try {} catch(NaN) {} }; }
|
||||
· ─┬─
|
||||
· ╰── Shadowing of global property 'NaN'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'NaN'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function NaN(NaN) { var NaN; !function NaN(NaN) { try {} catch(NaN) {} }; }
|
||||
· ─┬─
|
||||
· ╰── Shadowing of global property 'NaN'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'NaN'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function NaN(NaN) { var NaN; !function NaN(NaN) { try {} catch(NaN) {} }; }
|
||||
· ─┬─
|
||||
· ╰── Shadowing of global property 'NaN'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'NaN'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function NaN(NaN) { var NaN; !function NaN(NaN) { try {} catch(NaN) {} }; }
|
||||
· ─┬─
|
||||
· ╰── Shadowing of global property 'NaN'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'undefined'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function undefined(undefined) { !function undefined(undefined) { try {} catch(undefined) {} }; }
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'undefined'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'undefined'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function undefined(undefined) { !function undefined(undefined) { try {} catch(undefined) {} }; }
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'undefined'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'undefined'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function undefined(undefined) { !function undefined(undefined) { try {} catch(undefined) {} }; }
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'undefined'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'undefined'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function undefined(undefined) { !function undefined(undefined) { try {} catch(undefined) {} }; }
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'undefined'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'Infinity'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function Infinity(Infinity) { var Infinity; !function Infinity(Infinity) { try {} catch(Infinity) {} }; }
|
||||
· ────┬───
|
||||
· ╰── Shadowing of global property 'Infinity'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'Infinity'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function Infinity(Infinity) { var Infinity; !function Infinity(Infinity) { try {} catch(Infinity) {} }; }
|
||||
· ────┬───
|
||||
· ╰── Shadowing of global property 'Infinity'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'Infinity'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function Infinity(Infinity) { var Infinity; !function Infinity(Infinity) { try {} catch(Infinity) {} }; }
|
||||
· ────┬───
|
||||
· ╰── Shadowing of global property 'Infinity'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'Infinity'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function Infinity(Infinity) { var Infinity; !function Infinity(Infinity) { try {} catch(Infinity) {} }; }
|
||||
· ────┬───
|
||||
· ╰── Shadowing of global property 'Infinity'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'arguments'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function arguments(arguments) { var arguments; !function arguments(arguments) { try {} catch(arguments) {} }; }
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'arguments'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'arguments'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function arguments(arguments) { var arguments; !function arguments(arguments) { try {} catch(arguments) {} }; }
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'arguments'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'arguments'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function arguments(arguments) { var arguments; !function arguments(arguments) { try {} catch(arguments) {} }; }
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'arguments'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'arguments'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function arguments(arguments) { var arguments; !function arguments(arguments) { try {} catch(arguments) {} }; }
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'arguments'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'eval'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function eval(eval) { var eval; !function eval(eval) { try {} catch(eval) {} }; }
|
||||
· ──┬─
|
||||
· ╰── Shadowing of global property 'eval'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'eval'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function eval(eval) { var eval; !function eval(eval) { try {} catch(eval) {} }; }
|
||||
· ──┬─
|
||||
· ╰── Shadowing of global property 'eval'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'eval'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function eval(eval) { var eval; !function eval(eval) { try {} catch(eval) {} }; }
|
||||
· ──┬─
|
||||
· ╰── Shadowing of global property 'eval'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'eval'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ function eval(eval) { var eval; !function eval(eval) { try {} catch(eval) {} }; }
|
||||
· ──┬─
|
||||
· ╰── Shadowing of global property 'eval'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'eval'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ var eval = (eval) => { var eval; !function eval(eval) { try {} catch(eval) {} }; }
|
||||
· ──┬─
|
||||
· ╰── Shadowing of global property 'eval'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'eval'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ var eval = (eval) => { var eval; !function eval(eval) { try {} catch(eval) {} }; }
|
||||
· ──┬─
|
||||
· ╰── Shadowing of global property 'eval'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'eval'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ var eval = (eval) => { var eval; !function eval(eval) { try {} catch(eval) {} }; }
|
||||
· ──┬─
|
||||
· ╰── Shadowing of global property 'eval'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'eval'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ var eval = (eval) => { var eval; !function eval(eval) { try {} catch(eval) {} }; }
|
||||
· ──┬─
|
||||
· ╰── Shadowing of global property 'eval'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'undefined'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ var [undefined] = [1]
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'undefined'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'undefined'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ var {undefined} = obj; var {a: undefined} = obj; var {a: {b: {undefined}}} = obj; var {a, ...undefined} = obj;
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'undefined'
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-shadow-restricted-names): Shadowing of global property 'undefined'
|
||||
╭─[no_shadow_restricted_names.tsx:1:1]
|
||||
1 │ var undefined; undefined = 5;
|
||||
· ────┬────
|
||||
· ╰── Shadowing of global property 'undefined'
|
||||
╰────
|
||||
|
||||
Loading…
Reference in a new issue