From 864efe0c2bb98255973f564bd9e8254316fba03f Mon Sep 17 00:00:00 2001 From: "jordan.boyer" Date: Wed, 3 Jul 2024 08:56:55 +0200 Subject: [PATCH] feat(linter): add rule no-undefined related to #479 --- crates/oxc_linter/src/rules.rs | 2 + .../src/rules/eslint/no_undefined.rs | 145 ++++++++++ .../src/snapshots/no_undefined.snap | 247 ++++++++++++++++++ 3 files changed, 394 insertions(+) create mode 100644 crates/oxc_linter/src/rules/eslint/no_undefined.rs create mode 100644 crates/oxc_linter/src/snapshots/no_undefined.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index abcfbaeec9607..90c6424ab7418 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -96,6 +96,7 @@ mod eslint { pub mod no_ternary; pub mod no_this_before_super; pub mod no_undef; + pub mod no_undefined; pub mod no_unreachable; pub mod no_unsafe_finally; pub mod no_unsafe_negation; @@ -494,6 +495,7 @@ oxc_macros::declare_all_lint_rules! { eslint::no_shadow_restricted_names, eslint::no_sparse_arrays, eslint::no_undef, + eslint::no_undefined, eslint::no_unreachable, eslint::no_unsafe_finally, eslint::no_unsafe_negation, diff --git a/crates/oxc_linter/src/rules/eslint/no_undefined.rs b/crates/oxc_linter/src/rules/eslint/no_undefined.rs new file mode 100644 index 0000000000000..13c724dc6345e --- /dev/null +++ b/crates/oxc_linter/src/rules/eslint/no_undefined.rs @@ -0,0 +1,145 @@ +use oxc_ast::AstKind; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +#[derive(Debug, Default, Clone)] +pub struct NoUndefined; + +fn no_undefined_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("eslint(no-undefined): Disallow the use of `undefined` as an identifier") + .with_help("Unexpected use of undefined.") + .with_label(span0) +} + +declare_oxc_lint!( + /// ### What it does + /// Disallow the use of `undefined` as an identifier + /// + /// ### Why is this bad? + /// + /// + /// ### Example of bad code + /// ```javascript + /// + /// var foo = undefined; + /// + /// var undefined = "foo"; + /// + /// if (foo === undefined) { + /// ... + /// } + /// + /// function baz(undefined) { + /// ... + /// } + /// + /// bar(undefined, "lorem"); + /// + /// ``` + /// + /// ### Example of good code + /// ```javascript + /// var foo = void 0; + /// + /// var Undefined = "foo"; + /// + /// if (typeof foo === "undefined") { + /// ... + /// } + /// + /// global.undefined = "foo"; + /// + /// bar(void 0, "lorem"); + /// ``` + /// + NoUndefined, + restriction, +); + +fn diagnostic_undefined_keyword(name: &str, span0: Span, ctx: &LintContext) { + if name == "undefined" { + ctx.diagnostic(no_undefined_diagnostic(span0)); + } +} + +impl Rule for NoUndefined { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + match node.kind() { + AstKind::IdentifierReference(ident) => { + diagnostic_undefined_keyword(ident.name.as_str(), ident.span, ctx); + } + AstKind::BindingIdentifier(ident) => { + diagnostic_undefined_keyword(ident.name.as_str(), ident.span, ctx); + } + _ => {} + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "void 0", + "void!0", + "void-0", + "void+0", + "null", + "undefine", + "a.undefined", + "this.undefined", + "global['undefined']", + "({ undefined: bar })", + "({ undefined: bar } = foo)", + "({ undefined() {} })", + "class Foo { undefined() {} }", + "(class { undefined() {} })", + "import { undefined as a } from 'foo'", // ES6_MODULE, + "export { undefined } from 'foo'", // ES6_MODULE, + "export { undefined as a } from 'foo'", // ES6_MODULE, + "export { a as undefined } from 'foo'", // ES6_MODULE + ]; + + let fail = vec![ + "undefined", + "undefined.a", + "a[undefined]", + "undefined[0]", + "f(undefined)", + "function f(undefined) {}", + "function f() { var undefined; }", + "function f() { undefined = true; }", + "var undefined;", + "try {} catch(undefined) {}", + "function undefined() {}", + "(function undefined(){}())", + "var foo = function undefined() {}", + "foo = function undefined() {}", + "undefined = true", + "var undefined = true", + "({ undefined })", + "({ [undefined]: foo })", + "({ bar: undefined })", + "({ bar: undefined } = foo)", + "var { undefined } = foo", + "var { bar: undefined } = foo", + "({ undefined: function undefined() {} })", + "({ foo: function undefined() {} })", + "class Foo { [undefined]() {} }", + "(class { [undefined]() {} })", + "var undefined = true; undefined = false;", + "import undefined from 'foo'", // ES6_MODULE, + "import * as undefined from 'foo'", // ES6_MODULE, + "import { undefined } from 'foo'", // ES6_MODULE, + "import { a as undefined } from 'foo'", // ES6_MODULE, + "let a = [b, ...undefined]", + "[a, ...undefined] = b", + "[a = undefined] = b", + ]; + + Tester::new(NoUndefined::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_undefined.snap b/crates/oxc_linter/src/snapshots/no_undefined.snap new file mode 100644 index 0000000000000..9f4405138be99 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_undefined.snap @@ -0,0 +1,247 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:1] + 1 │ undefined + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:1] + 1 │ undefined.a + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:3] + 1 │ a[undefined] + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:1] + 1 │ undefined[0] + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:3] + 1 │ f(undefined) + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:12] + 1 │ function f(undefined) {} + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:20] + 1 │ function f() { var undefined; } + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:16] + 1 │ function f() { undefined = true; } + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:5] + 1 │ var undefined; + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:14] + 1 │ try {} catch(undefined) {} + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:10] + 1 │ function undefined() {} + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:11] + 1 │ (function undefined(){}()) + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:20] + 1 │ var foo = function undefined() {} + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:16] + 1 │ foo = function undefined() {} + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:1] + 1 │ undefined = true + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:5] + 1 │ var undefined = true + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:4] + 1 │ ({ undefined }) + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:5] + 1 │ ({ [undefined]: foo }) + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:9] + 1 │ ({ bar: undefined }) + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:9] + 1 │ ({ bar: undefined } = foo) + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:7] + 1 │ var { undefined } = foo + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:12] + 1 │ var { bar: undefined } = foo + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:24] + 1 │ ({ undefined: function undefined() {} }) + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:18] + 1 │ ({ foo: function undefined() {} }) + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:14] + 1 │ class Foo { [undefined]() {} } + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:11] + 1 │ (class { [undefined]() {} }) + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:5] + 1 │ var undefined = true; undefined = false; + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:23] + 1 │ var undefined = true; undefined = false; + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:8] + 1 │ import undefined from 'foo' + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:13] + 1 │ import * as undefined from 'foo' + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:10] + 1 │ import { undefined } from 'foo' + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:15] + 1 │ import { a as undefined } from 'foo' + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:16] + 1 │ let a = [b, ...undefined] + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:8] + 1 │ [a, ...undefined] = b + · ───────── + ╰──── + help: Unexpected use of undefined. + + ⚠ eslint(no-undefined): Disallow the use of `undefined` as an identifier + ╭─[no_undefined.tsx:1:6] + 1 │ [a = undefined] = b + · ───────── + ╰──── + help: Unexpected use of undefined.