Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ impl NoUnusedVars {
return true;
}

if self.ignore_using_declarations && decl.kind.is_using() {
return true;
}

false
}

Expand Down
30 changes: 30 additions & 0 deletions crates/oxc_linter/src/rules/eslint/no_unused_vars/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,26 @@ pub struct NoUnusedVarsOptions {
/// ```
pub ignore_class_with_static_init_block: bool,

/// The `ignoreUsingDeclarations` option is a boolean (default: `false`).
/// When set to `true`, the rule will ignore variables declared with
/// `using` or `await using` declarations, even if they are unused.
///
/// This is useful when working with resources that need to be disposed
/// via the explicit resource management proposal, where the primary
/// purpose is the disposal side effect rather than using the resource.
///
/// ## Example
///
/// Examples of **correct** code for the `{ "ignoreUsingDeclarations": true }` option:
///
/// ```javascript
/// /*eslint no-unused-vars: ["error", { "ignoreUsingDeclarations": true }]*/
///
/// using resource = getResource();
/// await using anotherResource = getAnotherResource();
/// ```
pub ignore_using_declarations: bool,

/// The `reportUsedIgnorePattern` option is a boolean (default: `false`).
/// Using this option will report variables that match any of the valid
/// ignore pattern options (`varsIgnorePattern`, `argsIgnorePattern`,
Expand Down Expand Up @@ -341,6 +361,7 @@ impl Default for NoUnusedVarsOptions {
caught_errors_ignore_pattern: IgnorePattern::None,
destructured_array_ignore_pattern: IgnorePattern::None,
ignore_class_with_static_init_block: false,
ignore_using_declarations: false,
report_used_ignore_pattern: false,
report_vars_only_used_as_types: false,
}
Expand Down Expand Up @@ -579,6 +600,11 @@ impl TryFrom<Value> for NoUnusedVarsOptions {
.map_or(Some(false), Value::as_bool)
.unwrap_or(false);

let ignore_using_declarations: bool = config
.get("ignoreUsingDeclarations")
.map_or(Some(false), Value::as_bool)
.unwrap_or(false);

let report_used_ignore_pattern: bool = config
.get("reportUsedIgnorePattern")
.map_or(Some(false), Value::as_bool)
Expand All @@ -599,6 +625,7 @@ impl TryFrom<Value> for NoUnusedVarsOptions {
caught_errors_ignore_pattern,
destructured_array_ignore_pattern,
ignore_class_with_static_init_block,
ignore_using_declarations,
report_used_ignore_pattern,
report_vars_only_used_as_types,
})
Expand Down Expand Up @@ -629,6 +656,7 @@ mod tests {
assert!(rule.destructured_array_ignore_pattern.is_none());
assert!(!rule.ignore_rest_siblings);
assert!(!rule.ignore_class_with_static_init_block);
assert!(!rule.ignore_using_declarations);
assert!(!rule.report_used_ignore_pattern);
}

Expand Down Expand Up @@ -668,6 +696,7 @@ mod tests {
assert!(rule.destructured_array_ignore_pattern.is_default());
assert!(rule.ignore_rest_siblings);
assert!(!rule.ignore_class_with_static_init_block);
assert!(!rule.ignore_using_declarations);
assert!(rule.report_used_ignore_pattern);
}

Expand Down Expand Up @@ -707,6 +736,7 @@ mod tests {
.try_into()
.unwrap();
assert!(rule.ignore_rest_siblings);
assert!(!rule.ignore_using_declarations);
// an options object is provided, so no default pattern is set.
assert!(rule.vars_ignore_pattern.is_none());
}
Expand Down
151 changes: 150 additions & 1 deletion crates/oxc_linter/src/rules/eslint/no_unused_vars/tests/eslint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,20 @@ fn test() {
Some(
serde_json::json!([{ "destructuredArrayIgnorePattern": "^_", "reportUsedIgnorePattern": true }]),
),
), // { "ecmaVersion": 6 }
), // { "ecmaVersion": 6 },
(
"using resource = getResource();
resource;",
None,
), // { "sourceType": "module", "ecmaVersion": 2026, },
(
"using resource = getResource();",
Some(serde_json::json!([{ "ignoreUsingDeclarations": true }])),
), // { "sourceType": "module", "ecmaVersion": 2026, },
(
"await using resource = getResource();",
Some(serde_json::json!([{ "ignoreUsingDeclarations": true }])),
), // { "sourceType": "module", "ecmaVersion": 2026, }
];

let fail = vec![
Expand Down Expand Up @@ -589,6 +602,8 @@ fn test() {
("(function(obj) { var name; for ( name in obj ) { i(); return; } })({});", None),
("(function(obj) { var name; for ( name in obj ) { } })({});", None),
("(function(obj) { for ( var name in obj ) { } })({});", None),
("for ( var { foo } in bar ) { }", None), // { "ecmaVersion": 6 },
("for ( var [ foo ] in bar ) { }", None), // { "ecmaVersion": 6 },
("(function(iter) { var name; for ( name of iter ) { i(); return; } })({});", None), // { "ecmaVersion": 6 },
("(function(iter) { var name; for ( name of iter ) { } })({});", None), // { "ecmaVersion": 6 },
("(function(iter) { for ( var name of iter ) { } })({});", None), // { "ecmaVersion": 6 },
Expand Down Expand Up @@ -725,6 +740,7 @@ fn test() {
("var a = 2, b = 4; a = a * 2 + b;", None),
// https://github.com/oxc-project/oxc/issues/4436
("function foo(cb) { cb = function(a) { cb(1 + a); }; bar(not_cb); } foo();", None),
// ("function foo(cb) { cb = function(a) { return cb(1 + a); }(); } foo();", None),
("function foo(cb) { cb = (function(a) { cb(1 + a); }, cb); } foo();", None),
// ("function foo(cb) { cb = (0, function(a) { cb(1 + a); }); } foo();", None),
(
Expand Down Expand Up @@ -975,6 +991,139 @@ fn test() {
",
Some(serde_json::json!([{ "argsIgnorePattern": "ignored", "varsIgnorePattern": "_" }])),
), // { "ecmaVersion": 2015 }
("const [a, b, c] = foo; alert(a + c);", None), // { "ecmaVersion": 6 },
("const [a = aDefault] = foo;", None), // { "ecmaVersion": 6 },
("const [[a = aDefault]]= foo;", None), // { "ecmaVersion": 6 },
("const [[a = aDefault], b]= foo;", None), // { "ecmaVersion": 6 },
("const [a = aDefault, b] = foo; alert(b);", None), // { "ecmaVersion": 6 },
("function a([a = aDefault]) { } a();", None), // { "ecmaVersion": 6 },
("function a([[a = aDefault]]) { } a();", None), // { "ecmaVersion": 6 },
("function a([a = aDefault, b]) { alert(b); } a();", None), // { "ecmaVersion": 6 },
("function a([[a = aDefault, b]]) { alert(b); } a();", None), // { "ecmaVersion": 6 },
("const { a: a1 } = foo", None), // { "ecmaVersion": 6 },
("const { a: a1, b: b1 } = foo; alert(b1);", None), // { "ecmaVersion": 6 },
("const { a: a1, b: b1 } = foo; alert(a1);", None), // { "ecmaVersion": 6 },
("function a({ a: a1 }) {} a();", None), // { "ecmaVersion": 6 },
("const { a: a1 = aDefault } = foo;", None), // { "ecmaVersion": 6 },
("const [{ a: a1 = aDefault }] = foo;", None), // { "ecmaVersion": 6 },
("const { a = aDefault } = foo;", None), // { "ecmaVersion": 6 },
("const { a = aDefault, b } = foo; alert(b);", None), // { "ecmaVersion": 6 },
("const { a, b = bDefault } = foo; alert(a);", None), // { "ecmaVersion": 6 },
("const { a, b = bDefault, c } = foo; alert(a + c);", None), // { "ecmaVersion": 6 },
("const { [key]: a } = foo;", None), // { "ecmaVersion": 6 },
("const [...{ a, b }] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("function foo (...rest) {} foo();", None), // { "ecmaVersion": 2023 },
("function foo (a, ...rest) { alert(a); } foo();", None), // { "ecmaVersion": 2023 },
("const {...rest} = foo;", None), // { "ecmaVersion": 2023 },
("const {a, ...rest} = foo; alert(a);", None), // { "ecmaVersion": 2023 },
("const {...rest} = foo, a = bar; alert(a);", None), // { "ecmaVersion": 2023 },
("const a = bar, {...rest} = foo; alert(a);", None), // { "ecmaVersion": 2023 },
("function foo ({...rest}) { } foo();", None), // { "ecmaVersion": 2023 },
("function foo (a, {...rest}) { alert(a); } foo();", None), // { "ecmaVersion": 2023 },
("function foo ({...rest}, a) { alert(a); } foo();", None), // { "ecmaVersion": 2023 },
("const [...rest] = foo;", None), // { "ecmaVersion": 2023 },
("const [[...rest]] = foo;", None), // { "ecmaVersion": 2023 },
("const [a, ...rest] = foo; alert(a);", None), // { "ecmaVersion": 2023 },
("function foo ([...rest]) { } foo();", None), // { "ecmaVersion": 2023 },
("const [a, ...{ b }] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [[a, ...{ b }]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [...[a]] = array;", None), // { "ecmaVersion": 2023 },
("const [[...[a]]] = array;", None), // { "ecmaVersion": 2023 },
("const [...[a, b]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [a, ...[b]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [[a, ...[b]]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [a, ...[b]] = array; alert(b);", None), // { "ecmaVersion": 2023 },
("const [a, ...[[ b ]]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [a, ...[{ b }]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("function foo([a, ...[[ b ]]]) {} foo();", None), // { "ecmaVersion": 2023 },
("function foo([a, ...[{ b }]]) {} foo();", None), // { "ecmaVersion": 2023 },
("function foo(...[[ a ]]) {} foo();", None), // { "ecmaVersion": 2023 },
("function foo(...[{ a }]) {} foo();", None), // { "ecmaVersion": 2023 },
("function foo(a, ...[b]) { alert(a); } foo();", None), // { "ecmaVersion": 2023 },
("const [a, [b]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [[a, [b]]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [a, [[b]]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("function a([[b]]) {} a();", None), // { "ecmaVersion": 2023 },
("function a([[b], c]) { alert(c); } a();", None), // { "ecmaVersion": 2023 },
("const [{b}, a] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [[{b}, a]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("const [[[{b}], a]] = array; alert(a);", None), // { "ecmaVersion": 2023 },
("function a([{b}]) {} a();", None), // { "ecmaVersion": 2023 },
("function a([{b}, c]) { alert(c); } a();", None), // { "ecmaVersion": 2023 },
("const { a: { b }, c } = foo; alert(c);", None), // { "ecmaVersion": 2023 },
("const { c, a: { b } } = foo; alert(c);", None), // { "ecmaVersion": 2023 },
("const { a: { b: { c }, d } } = foo; alert(d);", None), // { "ecmaVersion": 2023 },
("const { a: { b: { c: { e } }, d } } = foo; alert(d);", None), // { "ecmaVersion": 2023 },
("const [{ a: { b }, c }] = foo; alert(c);", None), // { "ecmaVersion": 2023 },
("const { a: [{ b }]} = foo;", None), // { "ecmaVersion": 2023 },
("const { a: [[ b ]]} = foo;", None), // { "ecmaVersion": 2023 },
("const [{ a: [{ b }]}] = foo;", None), // { "ecmaVersion": 2023 },
("const { a: [{ b }], c} = foo; alert(c);", None), // { "ecmaVersion": 2023 },
("function foo({ a: [{ b }]}) {} foo();", None), // { "ecmaVersion": 2023 },
("function foo({ a: [[ b ]]}) {} foo();", None), // { "ecmaVersion": 2023 },
("let a = foo, b = 'bar'; alert(b);", None), // { "ecmaVersion": 2023 },
("let a = foo, b = 'bar'; alert(a);", None), // { "ecmaVersion": 2023 },
("let { a } = foo, bar = 'hello'; alert(bar);", None), // { "ecmaVersion": 2023 },
("let bar = 'hello', { a } = foo; alert(bar);", None), // { "ecmaVersion": 2023 },
("import a from 'module';", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import * as foo from 'module';", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import a, * as foo from 'module'; a();", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import a, * as foo from 'module'; foo.hello;", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import { a } from 'module';", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import { a, b } from 'module'; alert(b);", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import { a, b } from 'module'; alert(a);", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import { a as foo } from 'module';", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import { a as foo, b } from 'module'; alert(b);", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import { a, b as foo } from 'module'; alert(a);", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import { default as foo, a } from 'module'; alert(a);", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import foo, { a } from 'module'; alert(a);", None), // { "ecmaVersion": 6, "sourceType": "module" },
("import foo, { a } from 'module'; foo();", None), // { "ecmaVersion": 6, "sourceType": "module" },
("let a; a = foo;", None), // { "ecmaVersion": 6 },
("array.forEach(a => {})", None), // { "ecmaVersion": 6 },
("if (foo()) var bar;", None), // { "ecmaVersion": 6 },
("for (;;) var foo;", None), // { "ecmaVersion": 6 },
("for (a in b) var foo;", None), // { "ecmaVersion": 6 },
("for (a of b) var foo;", None), // { "ecmaVersion": 6 },
("while (a) var foo;", None), // { "ecmaVersion": 6 },
("do var foo; while (b);", None), // { "ecmaVersion": 6 },
("with (a) var foo;", None), // { "ecmaVersion": 6 },
("var a;'use strict';b(00);", None),
("var [a] = foo;'use strict';b(00);", None), // { "ecmaVersion": 6 },
("var [...a] = foo;'use strict';b(00);", None), // { "ecmaVersion": 6 },
("var {a} = foo;'use strict';b(00);", None), // { "ecmaVersion": 6 },
(
"console.log('foo')
var a
+b > 0 ? bar() : baz()",
None,
),
(
"console.log('foo')
var [a] = foo;
+b > 0 ? bar() : baz()",
None,
), // { "ecmaVersion": 6 },
(
"console.log('foo')
var {a} = foo;
+b > 0 ? bar() : baz()",
None,
), // { "ecmaVersion": 6 },
(
"let x;
() => x = 1;",
None,
), // { "ecmaVersion": 6 },
(
"let [a = 1] = arr;
a = 2;",
None,
), // { "ecmaVersion": 6 },
// ("function foo(a = 1, b){alert(b);} foo();", None), // { "ecmaVersion": 6 },
("function foo(a = 1) {a = 2;} foo();", None), // { "ecmaVersion": 6 },
("function foo(a = 1, b) {a = 2;} foo();", None), // { "ecmaVersion": 6 },
("using resource = getResource();", None), // { "sourceType": "module", "ecmaVersion": 2026, },
("await using resource = getResource();", None), // { "sourceType": "module", "ecmaVersion": 2026, }
];

Tester::new(NoUnusedVars::NAME, NoUnusedVars::PLUGIN, pass, fail)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,15 +526,20 @@ fn test_vars_catch() {

#[test]
fn test_vars_using() {
let pass = vec![("using a = 1; console.log(a)", None)];
let pass = vec![
("using a = 1; console.log(a)", None),
("using a = 1;", Some(serde_json::json!([{ "ignoreUsingDeclarations": true }]))),
("await using a = 1;", Some(serde_json::json!([{ "ignoreUsingDeclarations": true }]))),
];

let fail = vec![("using a = 1;", None)];
let fail = vec![("using a = 1;", None), ("await using a = 1;", None)];

Tester::new(NoUnusedVars::NAME, NoUnusedVars::PLUGIN, pass, fail)
.intentionally_allow_no_fix_tests()
.with_snapshot_suffix("oxc-vars-using")
.test_and_snapshot();
}

#[test]
fn test_functions() {
let pass = vec![
Expand Down
Loading
Loading