From 054847b308fe34e8f00aecb19d26802dbc10f02e Mon Sep 17 00:00:00 2001 From: yefan Date: Thu, 29 May 2025 11:12:00 +0800 Subject: [PATCH 01/11] implement --- crates/oxc_linter/src/rules.rs | 2 + .../src/rules/eslint/no_unassigned_vars.rs | 148 ++++++++++++++++++ .../snapshots/eslint_no_unassigned_vars.snap | 119 ++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs create mode 100644 crates/oxc_linter/src/snapshots/eslint_no_unassigned_vars.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index ee77410e62dda..ae70b21ba6604 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -138,6 +138,7 @@ mod eslint { pub mod no_ternary; pub mod no_this_before_super; pub mod no_throw_literal; + pub mod no_unassigned_vars; pub mod no_undef; pub mod no_undefined; pub mod no_unexpected_multiline; @@ -592,6 +593,7 @@ oxc_macros::declare_all_lint_rules! { eslint::max_nested_callbacks, eslint::max_params, eslint::new_cap, + eslint::no_unassigned_vars, eslint::no_alert, eslint::no_array_constructor, eslint::no_async_promise_executor, diff --git a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs new file mode 100644 index 0000000000000..ce64e2891335a --- /dev/null +++ b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs @@ -0,0 +1,148 @@ +use oxc_ast::{AstKind, ast::BindingPatternKind}; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{AstNode, context::LintContext, rule::Rule}; + +fn no_unassigned_vars_diagnostic(span: Span, msg: String) -> OxcDiagnostic { + OxcDiagnostic::warn(msg) + .with_help("Variable declared without assignment. Either assign a value or remove the declaration.") + .with_label(span) +} + +#[derive(Debug, Default, Clone)] +pub struct NoUnassignedVars; + +declare_oxc_lint!( + /// ### What it does + /// + /// Disallow let or var variables that are read but never assigned + /// + /// ### Why is this bad? + /// + /// This rule flags let or var declarations that are never assigned a value but are still read or used in the code. + /// Since these variables will always be undefined, their usage is likely a programming mistake. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// let status; + /// if (status === 'ready') { + /// console.log('Ready!'); + /// } + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// let message = "hello"; + /// console.log(message); + /// + /// let user; + /// user = getUser(); + /// console.log(user.name); + /// ``` + NoUnassignedVars, + eslint, + suspicious, +); + +impl Rule for NoUnassignedVars { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::VariableDeclarator(declarator) = node.kind() else { + return; + }; + if declarator.init.is_some() || declarator.kind.is_const() { + return; + } + let Some(AstKind::VariableDeclaration(parent)) = ctx.nodes().parent_kind(node.id()) else { + return; + }; + if parent.declare { + return; + } + let BindingPatternKind::BindingIdentifier(ident) = &declarator.id.kind else { + return; + }; + let Some(symbol_id) = ident.symbol_id.take() else { + return; + }; + let mut has_read = false; + for reference in ctx.symbol_references(symbol_id) { + if reference.is_write() { + return; + } + if reference.is_read() { + has_read = true; + } + } + if has_read { + let msg = format!( + "'{}' is always 'undefined' because it's never assigned.", + ident.name.as_str() + ); + ctx.diagnostic(no_unassigned_vars_diagnostic(ident.span, msg)); + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "let x;", + "var x;", + "const x = undefined; console.log(x);", + "let y = undefined; console.log(y);", + "var y = undefined; console.log(y);", + "let a = x, b = y; console.log(a, b);", + "var a = x, b = y; console.log(a, b);", + "const foo = (two) => { let one; if (one !== two) one = two; }", + // typescript + "let z: number | undefined = undefined; log(z);", + "declare let c: string | undefined; log(c);", + ]; + + let fail = vec![ + r" + let status; + if (status === 'ready') { + console.log('Ready!'); + } + + let user; + greet(user); + + function test() { + let error; + return error || 'Unknown error'; + } + + let options; + const { debug } = options || {}; + + let flag; + while (!flag) { + // Do something... + } + + let config; + function init() { + return config?.enabled; + } + ", + "let x; let a = x, b; console.log(x, a, b);", + "const foo = (two) => { let one; if (one === two) {} }", + "function test() { let error; return error || 'Unknown error'; }", + "let options; const { debug } = options || {};", + "let flag; while (!flag) { }", + "let config; function init() { return config?.enabled; }", + // typescript + "let z: number | undefined; console.log(z);", + "const foo = (two: string): void => { let one: string | undefined; if (one === two) {} }", + ]; + + Tester::new(NoUnassignedVars::NAME, NoUnassignedVars::PLUGIN, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/eslint_no_unassigned_vars.snap b/crates/oxc_linter/src/snapshots/eslint_no_unassigned_vars.snap new file mode 100644 index 0000000000000..b79ecfaf524bc --- /dev/null +++ b/crates/oxc_linter/src/snapshots/eslint_no_unassigned_vars.snap @@ -0,0 +1,119 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ eslint(no-unassigned-vars): 'status' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:2:17] + 1 │ + 2 │ let status; + · ────── + 3 │ if (status === 'ready') { + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'user' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:7:17] + 6 │ + 7 │ let user; + · ──── + 8 │ greet(user); + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'error' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:11:21] + 10 │ function test() { + 11 │ let error; + · ───── + 12 │ return error || 'Unknown error'; + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'options' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:15:17] + 14 │ + 15 │ let options; + · ─────── + 16 │ const { debug } = options || {}; + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'flag' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:18:17] + 17 │ + 18 │ let flag; + · ──── + 19 │ while (!flag) { + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'config' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:23:17] + 22 │ + 23 │ let config; + · ────── + 24 │ function init() { + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'x' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:5] + 1 │ let x; let a = x, b; console.log(x, a, b); + · ─ + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'b' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:19] + 1 │ let x; let a = x, b; console.log(x, a, b); + · ─ + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'one' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:28] + 1 │ const foo = (two) => { let one; if (one === two) {} } + · ─── + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'error' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:23] + 1 │ function test() { let error; return error || 'Unknown error'; } + · ───── + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'options' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:5] + 1 │ let options; const { debug } = options || {}; + · ─────── + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'flag' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:5] + 1 │ let flag; while (!flag) { } + · ──── + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'config' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:5] + 1 │ let config; function init() { return config?.enabled; } + · ────── + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'z' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:5] + 1 │ let z: number | undefined; console.log(z); + · ───────────────────── + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'one' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:42] + 1 │ const foo = (two: string): void => { let one: string | undefined; if (one === two) {} } + · ─────────────────────── + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. From cce24045ebd4ae92e94f96422f3b48a92cf25c1c Mon Sep 17 00:00:00 2001 From: yefan Date: Thu, 29 May 2025 11:32:25 +0800 Subject: [PATCH 02/11] update snap --- ...ixtures__extends_config_overrides@oxlint.snap | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap index d470982ed2650..818f17b3aa855 100644 --- a/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap @@ -21,23 +21,23 @@ working directory: fixtures/extends_config `---- help: Use `unknown` instead, this will force you to explicitly, and safely, assert the type is correct. - ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-is-valid.html\eslint-plugin-jsx-a11y(anchor-is-valid)]8;;\: Missing `href` attribute for the `a` element. - ,-[overrides/test.tsx:2:11] + x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-ambiguous-text.html\eslint-plugin-jsx-a11y(anchor-ambiguous-text)]8;;\: Ambiguous text within anchor, screen reader users rely on link text for context. + ,-[overrides/test.tsx:2:10] 1 | function component(): any { 2 | return click here; - : ^ + : ^^^^^^^^^^^^^^^^^ 3 | } `---- - help: Provide an `href` for the `a` element. + help: Avoid using ambiguous text like "click here", replace it with more descriptive text that provides context. - x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-ambiguous-text.html\eslint-plugin-jsx-a11y(anchor-ambiguous-text)]8;;\: Ambiguous text within anchor, screen reader users rely on link text for context. - ,-[overrides/test.tsx:2:10] + ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-is-valid.html\eslint-plugin-jsx-a11y(anchor-is-valid)]8;;\: Missing `href` attribute for the `a` element. + ,-[overrides/test.tsx:2:11] 1 | function component(): any { 2 | return click here; - : ^^^^^^^^^^^^^^^^^ + : ^ 3 | } `---- - help: Avoid using ambiguous text like "click here", replace it with more descriptive text that provides context. + help: Provide an `href` for the `a` element. Found 1 warning and 3 errors. Finished in ms on 2 files using 1 threads. From 5a4aaf592716de3420e29bc9d6a9f1478cc8f943 Mon Sep 17 00:00:00 2001 From: yefan Date: Thu, 29 May 2025 15:09:06 +0800 Subject: [PATCH 03/11] add test case --- crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs index ce64e2891335a..596a6ff18b164 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs @@ -94,6 +94,7 @@ fn test() { let pass = vec![ "let x;", "var x;", + "const x = 2", "const x = undefined; console.log(x);", "let y = undefined; console.log(y);", "var y = undefined; console.log(y);", From ae8b865a1af31fd278dcaa499f273529deb75898 Mon Sep 17 00:00:00 2001 From: yefan Date: Thu, 29 May 2025 19:15:51 +0800 Subject: [PATCH 04/11] update --- .../src/rules/eslint/no_unassigned_vars.rs | 69 +++++++-------- .../snapshots/eslint_no_unassigned_vars.snap | 85 ++++++------------- 2 files changed, 59 insertions(+), 95 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs index 596a6ff18b164..a327f289e4ad2 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs @@ -14,6 +14,7 @@ fn no_unassigned_vars_diagnostic(span: Span, msg: String) -> OxcDiagnostic { #[derive(Debug, Default, Clone)] pub struct NoUnassignedVars; +// See for documentation details. declare_oxc_lint!( /// ### What it does /// @@ -94,55 +95,49 @@ fn test() { let pass = vec![ "let x;", "var x;", - "const x = 2", - "const x = undefined; console.log(x);", - "let y = undefined; console.log(y);", - "var y = undefined; console.log(y);", - "let a = x, b = y; console.log(a, b);", - "var a = x, b = y; console.log(a, b);", + "const x = undefined; log(x);", + "let y = undefined; log(y);", + "var y = undefined; log(y);", + "let a = x, b = y; log(a, b);", + "var a = x, b = y; log(a, b);", "const foo = (two) => { let one; if (one !== two) one = two; }", - // typescript "let z: number | undefined = undefined; log(z);", "declare let c: string | undefined; log(c);", + // " + // const foo = (two: string): void => { + // let one: string | undefined; + // if (one !== two) { + // one = two; + // } + // } + // ", + // " + // declare module 'module' { + // import type { T } from 'module'; + // let x: T; + // export = x; + // } + // ", ]; let fail = vec![ - r" - let status; - if (status === 'ready') { - console.log('Ready!'); - } - - let user; - greet(user); - - function test() { - let error; - return error || 'Unknown error'; - } - - let options; - const { debug } = options || {}; - - let flag; - while (!flag) { - // Do something... - } - - let config; - function init() { - return config?.enabled; - } - ", - "let x; let a = x, b; console.log(x, a, b);", + "let x; let a = x, b; log(x, a, b);", "const foo = (two) => { let one; if (one === two) {} }", + "let user; greet(user);", "function test() { let error; return error || 'Unknown error'; }", "let options; const { debug } = options || {};", "let flag; while (!flag) { }", "let config; function init() { return config?.enabled; }", - // typescript - "let z: number | undefined; console.log(z);", + "let x: number; log(x);", + "let x: number | undefined; log(x);", "const foo = (two: string): void => { let one: string | undefined; if (one === two) {} }", + " + declare module 'module' { + let x: string; + } + let y: string; + console.log(y); + ", ]; Tester::new(NoUnassignedVars::NAME, NoUnassignedVars::PLUGIN, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/snapshots/eslint_no_unassigned_vars.snap b/crates/oxc_linter/src/snapshots/eslint_no_unassigned_vars.snap index b79ecfaf524bc..07e251e12ddf5 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_unassigned_vars.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_unassigned_vars.snap @@ -1,70 +1,16 @@ --- source: crates/oxc_linter/src/tester.rs --- - ⚠ eslint(no-unassigned-vars): 'status' is always 'undefined' because it's never assigned. - ╭─[no_unassigned_vars.tsx:2:17] - 1 │ - 2 │ let status; - · ────── - 3 │ if (status === 'ready') { - ╰──── - help: Variable declared without assignment. Either assign a value or remove the declaration. - - ⚠ eslint(no-unassigned-vars): 'user' is always 'undefined' because it's never assigned. - ╭─[no_unassigned_vars.tsx:7:17] - 6 │ - 7 │ let user; - · ──── - 8 │ greet(user); - ╰──── - help: Variable declared without assignment. Either assign a value or remove the declaration. - - ⚠ eslint(no-unassigned-vars): 'error' is always 'undefined' because it's never assigned. - ╭─[no_unassigned_vars.tsx:11:21] - 10 │ function test() { - 11 │ let error; - · ───── - 12 │ return error || 'Unknown error'; - ╰──── - help: Variable declared without assignment. Either assign a value or remove the declaration. - - ⚠ eslint(no-unassigned-vars): 'options' is always 'undefined' because it's never assigned. - ╭─[no_unassigned_vars.tsx:15:17] - 14 │ - 15 │ let options; - · ─────── - 16 │ const { debug } = options || {}; - ╰──── - help: Variable declared without assignment. Either assign a value or remove the declaration. - - ⚠ eslint(no-unassigned-vars): 'flag' is always 'undefined' because it's never assigned. - ╭─[no_unassigned_vars.tsx:18:17] - 17 │ - 18 │ let flag; - · ──── - 19 │ while (!flag) { - ╰──── - help: Variable declared without assignment. Either assign a value or remove the declaration. - - ⚠ eslint(no-unassigned-vars): 'config' is always 'undefined' because it's never assigned. - ╭─[no_unassigned_vars.tsx:23:17] - 22 │ - 23 │ let config; - · ────── - 24 │ function init() { - ╰──── - help: Variable declared without assignment. Either assign a value or remove the declaration. - ⚠ eslint(no-unassigned-vars): 'x' is always 'undefined' because it's never assigned. ╭─[no_unassigned_vars.tsx:1:5] - 1 │ let x; let a = x, b; console.log(x, a, b); + 1 │ let x; let a = x, b; log(x, a, b); · ─ ╰──── help: Variable declared without assignment. Either assign a value or remove the declaration. ⚠ eslint(no-unassigned-vars): 'b' is always 'undefined' because it's never assigned. ╭─[no_unassigned_vars.tsx:1:19] - 1 │ let x; let a = x, b; console.log(x, a, b); + 1 │ let x; let a = x, b; log(x, a, b); · ─ ╰──── help: Variable declared without assignment. Either assign a value or remove the declaration. @@ -76,6 +22,13 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Variable declared without assignment. Either assign a value or remove the declaration. + ⚠ eslint(no-unassigned-vars): 'user' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:5] + 1 │ let user; greet(user); + · ──── + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + ⚠ eslint(no-unassigned-vars): 'error' is always 'undefined' because it's never assigned. ╭─[no_unassigned_vars.tsx:1:23] 1 │ function test() { let error; return error || 'Unknown error'; } @@ -104,9 +57,16 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Variable declared without assignment. Either assign a value or remove the declaration. - ⚠ eslint(no-unassigned-vars): 'z' is always 'undefined' because it's never assigned. + ⚠ eslint(no-unassigned-vars): 'x' is always 'undefined' because it's never assigned. ╭─[no_unassigned_vars.tsx:1:5] - 1 │ let z: number | undefined; console.log(z); + 1 │ let x: number; log(x); + · ───────── + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'x' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:1:5] + 1 │ let x: number | undefined; log(x); · ───────────────────── ╰──── help: Variable declared without assignment. Either assign a value or remove the declaration. @@ -117,3 +77,12 @@ source: crates/oxc_linter/src/tester.rs · ─────────────────────── ╰──── help: Variable declared without assignment. Either assign a value or remove the declaration. + + ⚠ eslint(no-unassigned-vars): 'y' is always 'undefined' because it's never assigned. + ╭─[no_unassigned_vars.tsx:5:12] + 4 │ } + 5 │ let y: string; + · ───────── + 6 │ console.log(y); + ╰──── + help: Variable declared without assignment. Either assign a value or remove the declaration. From 485ff34be1bfbed63300950edd81166c5820a755 Mon Sep 17 00:00:00 2001 From: yefan Date: Wed, 4 Jun 2025 19:23:54 +0800 Subject: [PATCH 05/11] ignore TSModuleDeclaration --- .../src/rules/eslint/no_unassigned_vars.rs | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs index a327f289e4ad2..9a056e7da5d93 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs @@ -63,6 +63,14 @@ impl Rule for NoUnassignedVars { if parent.declare { return; } + if ctx + .nodes() + .ancestors(node.id()) + .skip(1) + .any(|ancestor| matches!(ancestor.kind(), AstKind::TSModuleDeclaration(_))) + { + return; + } let BindingPatternKind::BindingIdentifier(ident) = &declarator.id.kind else { return; }; @@ -103,21 +111,21 @@ fn test() { "const foo = (two) => { let one; if (one !== two) one = two; }", "let z: number | undefined = undefined; log(z);", "declare let c: string | undefined; log(c);", - // " - // const foo = (two: string): void => { - // let one: string | undefined; - // if (one !== two) { - // one = two; - // } - // } - // ", - // " - // declare module 'module' { - // import type { T } from 'module'; - // let x: T; - // export = x; - // } - // ", + " + const foo = (two: string): void => { + let one: string | undefined; + if (one !== two) { + one = two; + } + } + ", + " + declare module 'module' { + import type { T } from 'module'; + let x: T; + export = x; + } + ", ]; let fail = vec![ From 0d311ef5996f51541837da8c53fdfdf5080e6a21 Mon Sep 17 00:00:00 2001 From: yefan Date: Thu, 19 Jun 2025 15:41:48 +0800 Subject: [PATCH 06/11] update snap --- ...des_with_plugin_-c .oxlintrc.json@oxlint.snap | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/oxlint/src/snapshots/fixtures__overrides_with_plugin_-c .oxlintrc.json@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__overrides_with_plugin_-c .oxlintrc.json@oxlint.snap index b2bd915f423fa..cce2536c23f46 100644 --- a/apps/oxlint/src/snapshots/fixtures__overrides_with_plugin_-c .oxlintrc.json@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__overrides_with_plugin_-c .oxlintrc.json@oxlint.snap @@ -23,23 +23,23 @@ working directory: fixtures/overrides_with_plugin `---- help: Consider removing this declaration. - x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/valid-title.html\eslint-plugin-jest(valid-title)]8;;\: "Should not have an empty title" - ,-[index.test.ts:4:6] + ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/expect-expect.html\eslint-plugin-jest(expect-expect)]8;;\: Test has no assertions + ,-[index.test.ts:4:3] 3 | 4 | it("", () => {}); - : ^^ + : ^^ 5 | // ^ jest/no-valid-title error as explicitly set in the `.test.ts` override `---- - help: "Write a meaningful title for your test" + help: Add assertion(s) in this Test - ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/expect-expect.html\eslint-plugin-jest(expect-expect)]8;;\: Test has no assertions - ,-[index.test.ts:4:3] + x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/valid-title.html\eslint-plugin-jest(valid-title)]8;;\: "Should not have an empty title" + ,-[index.test.ts:4:6] 3 | 4 | it("", () => {}); - : ^^ + : ^^ 5 | // ^ jest/no-valid-title error as explicitly set in the `.test.ts` override `---- - help: Add assertion(s) in this Test + help: "Write a meaningful title for your test" Found 2 warnings and 2 errors. Finished in ms on 2 files with 87 rules using 1 threads. From f589feaa1efa893e2d7ee1297eeb58c2f0318726 Mon Sep 17 00:00:00 2001 From: yefan Date: Tue, 22 Jul 2025 19:54:02 +0800 Subject: [PATCH 07/11] update --- ...ixtures__extends_config_overrides@oxlint.snap | 16 ++++++++-------- ...des_with_plugin_-c .oxlintrc.json@oxlint.snap | 16 ++++++++-------- .../src/rules/eslint/no_unassigned_vars.rs | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap index 818f17b3aa855..d470982ed2650 100644 --- a/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap @@ -21,23 +21,23 @@ working directory: fixtures/extends_config `---- help: Use `unknown` instead, this will force you to explicitly, and safely, assert the type is correct. - x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-ambiguous-text.html\eslint-plugin-jsx-a11y(anchor-ambiguous-text)]8;;\: Ambiguous text within anchor, screen reader users rely on link text for context. - ,-[overrides/test.tsx:2:10] + ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-is-valid.html\eslint-plugin-jsx-a11y(anchor-is-valid)]8;;\: Missing `href` attribute for the `a` element. + ,-[overrides/test.tsx:2:11] 1 | function component(): any { 2 | return click here; - : ^^^^^^^^^^^^^^^^^ + : ^ 3 | } `---- - help: Avoid using ambiguous text like "click here", replace it with more descriptive text that provides context. + help: Provide an `href` for the `a` element. - ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-is-valid.html\eslint-plugin-jsx-a11y(anchor-is-valid)]8;;\: Missing `href` attribute for the `a` element. - ,-[overrides/test.tsx:2:11] + x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-ambiguous-text.html\eslint-plugin-jsx-a11y(anchor-ambiguous-text)]8;;\: Ambiguous text within anchor, screen reader users rely on link text for context. + ,-[overrides/test.tsx:2:10] 1 | function component(): any { 2 | return click here; - : ^ + : ^^^^^^^^^^^^^^^^^ 3 | } `---- - help: Provide an `href` for the `a` element. + help: Avoid using ambiguous text like "click here", replace it with more descriptive text that provides context. Found 1 warning and 3 errors. Finished in ms on 2 files using 1 threads. diff --git a/apps/oxlint/src/snapshots/fixtures__overrides_with_plugin_-c .oxlintrc.json@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__overrides_with_plugin_-c .oxlintrc.json@oxlint.snap index cce2536c23f46..b2bd915f423fa 100644 --- a/apps/oxlint/src/snapshots/fixtures__overrides_with_plugin_-c .oxlintrc.json@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__overrides_with_plugin_-c .oxlintrc.json@oxlint.snap @@ -23,23 +23,23 @@ working directory: fixtures/overrides_with_plugin `---- help: Consider removing this declaration. - ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/expect-expect.html\eslint-plugin-jest(expect-expect)]8;;\: Test has no assertions - ,-[index.test.ts:4:3] + x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/valid-title.html\eslint-plugin-jest(valid-title)]8;;\: "Should not have an empty title" + ,-[index.test.ts:4:6] 3 | 4 | it("", () => {}); - : ^^ + : ^^ 5 | // ^ jest/no-valid-title error as explicitly set in the `.test.ts` override `---- - help: Add assertion(s) in this Test + help: "Write a meaningful title for your test" - x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/valid-title.html\eslint-plugin-jest(valid-title)]8;;\: "Should not have an empty title" - ,-[index.test.ts:4:6] + ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jest/expect-expect.html\eslint-plugin-jest(expect-expect)]8;;\: Test has no assertions + ,-[index.test.ts:4:3] 3 | 4 | it("", () => {}); - : ^^ + : ^^ 5 | // ^ jest/no-valid-title error as explicitly set in the `.test.ts` override `---- - help: "Write a meaningful title for your test" + help: Add assertion(s) in this Test Found 2 warnings and 2 errors. Finished in ms on 2 files with 87 rules using 1 threads. diff --git a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs index 9a056e7da5d93..1d0ed4b215819 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs @@ -57,7 +57,7 @@ impl Rule for NoUnassignedVars { if declarator.init.is_some() || declarator.kind.is_const() { return; } - let Some(AstKind::VariableDeclaration(parent)) = ctx.nodes().parent_kind(node.id()) else { + let AstKind::VariableDeclaration(parent) = ctx.nodes().parent_kind(node.id()) else { return; }; if parent.declare { From c1fd2b42a32e7d0ccfb7504e0a5bb1281b68c9b5 Mon Sep 17 00:00:00 2001 From: yefan Date: Tue, 22 Jul 2025 20:30:06 +0800 Subject: [PATCH 08/11] fix: linter benchmark --- crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs index 1d0ed4b215819..5ac5c91153406 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs @@ -74,9 +74,7 @@ impl Rule for NoUnassignedVars { let BindingPatternKind::BindingIdentifier(ident) = &declarator.id.kind else { return; }; - let Some(symbol_id) = ident.symbol_id.take() else { - return; - }; + let symbol_id = ident.symbol_id(); let mut has_read = false; for reference in ctx.symbol_references(symbol_id) { if reference.is_write() { From befae6b385dab8e70f05057e928baec4cd4c4496 Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Tue, 5 Aug 2025 13:26:37 +0100 Subject: [PATCH 09/11] remove ref --- crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs index 5ac5c91153406..c0bb6d1286d6d 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs @@ -14,7 +14,6 @@ fn no_unassigned_vars_diagnostic(span: Span, msg: String) -> OxcDiagnostic { #[derive(Debug, Default, Clone)] pub struct NoUnassignedVars; -// See for documentation details. declare_oxc_lint!( /// ### What it does /// From af43ce5f95ad62cc0692d750d813d90553d18778 Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Tue, 5 Aug 2025 13:27:34 +0100 Subject: [PATCH 10/11] change diagnostic --- .../src/rules/eslint/no_unassigned_vars.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs index c0bb6d1286d6d..52c32fb76c66d 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unassigned_vars.rs @@ -5,10 +5,14 @@ use oxc_span::Span; use crate::{AstNode, context::LintContext, rule::Rule}; -fn no_unassigned_vars_diagnostic(span: Span, msg: String) -> OxcDiagnostic { - OxcDiagnostic::warn(msg) - .with_help("Variable declared without assignment. Either assign a value or remove the declaration.") - .with_label(span) +fn no_unassigned_vars_diagnostic(span: Span, ident_name: &str) -> OxcDiagnostic { + OxcDiagnostic::warn(format!( + "'{ident_name}' is always 'undefined' because it's never assigned.", + )) + .with_help( + "Variable declared without assignment. Either assign a value or remove the declaration.", + ) + .with_label(span) } #[derive(Debug, Default, Clone)] @@ -84,11 +88,7 @@ impl Rule for NoUnassignedVars { } } if has_read { - let msg = format!( - "'{}' is always 'undefined' because it's never assigned.", - ident.name.as_str() - ); - ctx.diagnostic(no_unassigned_vars_diagnostic(ident.span, msg)); + ctx.diagnostic(no_unassigned_vars_diagnostic(ident.span, ident.name.as_str())); } } } From c85cdb4a37beabb7fb776e9daf61dd25572e0ad7 Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Tue, 5 Aug 2025 13:31:38 +0100 Subject: [PATCH 11/11] fix snapshot --- ...ixtures__extends_config_overrides@oxlint.snap | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap index d470982ed2650..818f17b3aa855 100644 --- a/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__extends_config_overrides@oxlint.snap @@ -21,23 +21,23 @@ working directory: fixtures/extends_config `---- help: Use `unknown` instead, this will force you to explicitly, and safely, assert the type is correct. - ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-is-valid.html\eslint-plugin-jsx-a11y(anchor-is-valid)]8;;\: Missing `href` attribute for the `a` element. - ,-[overrides/test.tsx:2:11] + x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-ambiguous-text.html\eslint-plugin-jsx-a11y(anchor-ambiguous-text)]8;;\: Ambiguous text within anchor, screen reader users rely on link text for context. + ,-[overrides/test.tsx:2:10] 1 | function component(): any { 2 | return click here; - : ^ + : ^^^^^^^^^^^^^^^^^ 3 | } `---- - help: Provide an `href` for the `a` element. + help: Avoid using ambiguous text like "click here", replace it with more descriptive text that provides context. - x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-ambiguous-text.html\eslint-plugin-jsx-a11y(anchor-ambiguous-text)]8;;\: Ambiguous text within anchor, screen reader users rely on link text for context. - ,-[overrides/test.tsx:2:10] + ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/jsx_a11y/anchor-is-valid.html\eslint-plugin-jsx-a11y(anchor-is-valid)]8;;\: Missing `href` attribute for the `a` element. + ,-[overrides/test.tsx:2:11] 1 | function component(): any { 2 | return click here; - : ^^^^^^^^^^^^^^^^^ + : ^ 3 | } `---- - help: Avoid using ambiguous text like "click here", replace it with more descriptive text that provides context. + help: Provide an `href` for the `a` element. Found 1 warning and 3 errors. Finished in ms on 2 files using 1 threads.