Skip to content

feat(lint): add ignore option to no-unused-variables#9397

Merged
dyc3 merged 13 commits intobiomejs:nextfrom
mvarendorff:feat/no-unused-variables-ignore-pattern
Mar 20, 2026
Merged

feat(lint): add ignore option to no-unused-variables#9397
dyc3 merged 13 commits intobiomejs:nextfrom
mvarendorff:feat/no-unused-variables-ignore-pattern

Conversation

@mvarendorff
Copy link
Copy Markdown

@mvarendorff mvarendorff commented Mar 8, 2026

No AI was used in the creation of this PR. If the changes are hideous, that's entirely my doing.

Summary

Adds a new option ignore to noUnusedVariables to exclude identifiers from the rule.

A discussion on this feature exists and a recent comment approved of the option.

A similar option is also available in ESLint.

My specific use-case for this is a DI-style setup where a directory is globbed and files dynamically loaded with await import(...). A decorator on classes in those files causes them to be registered to a DI provider however without this option, all classes are marked unused. With the option available, it becomes trivial to exclude these classes by means of a naming scheme (not perfect but better than disabling the check outright).

Test Plan

Snapshot tests have been added.

Docs

I hope they cover everything that's needed. Let me know if anything is missing!

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 8, 2026

🦋 Changeset detected

Latest commit: eeceada

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@biomejs/biome Minor
@biomejs/cli-win32-x64 Minor
@biomejs/cli-win32-arm64 Minor
@biomejs/cli-darwin-x64 Minor
@biomejs/cli-darwin-arm64 Minor
@biomejs/cli-linux-x64 Minor
@biomejs/cli-linux-arm64 Minor
@biomejs/cli-linux-x64-musl Minor
@biomejs/cli-linux-arm64-musl Minor
@biomejs/wasm-web Minor
@biomejs/wasm-bundler Minor
@biomejs/wasm-nodejs Minor
@biomejs/backend-jsonrpc Patch
@biomejs/js-api Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Mar 8, 2026
Copy link
Copy Markdown
Contributor

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to go against the next branch

Comment on lines +12 to +14
/// A Regex pattern to ignore matching variables from this rule with.
#[serde(skip_serializing_if = "Option::<_>::is_none")]
pub ignore_pattern: Option<Box<str>>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We try to ignore regex if possible because of the performance impact it can have.
  2. Do we have a use case for why it needs to be a regex or pattern?
  3. This needs to be a Option<Box<[...]>> so the user can supply multiple identifiers to ignore.

Realistically, I don't really see this being a regex to be super useful. The rule already ignores variables that begin with _, so if you want to have a bunch of unused variables all with different names, you can just add the prefix.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed you described your use case in your PR description, but its not super clear to me.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine making this a list for sure! I was just drawing inspiration the ESLint API which uses a single pattern.

Regex would conveniently allow specifying whether something is to be treated as a prefix or suffix for instance. In my use-case I want to ignore all .*Service$ for instance. I feel like treating the patterns as a .contains() might be a bit too drastic or is that what you had in mind? Generally fine with me but without regex taking the example above (i.e. an ignorePattern of Service), that would hit a variable called somethingServiceInstance as well which might not be intended.

I am open for ideas here!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a type called RestrictedRegex. We should use that one

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think a rough setup like this:

// service-decorator.js
export const Service = () => {
  return (targetClass) => {
    console.log('Registering service:', targetClass.name);

    return targetClass;
  };
};
// services/a.js
@Service()
class ExampleService {}
// services/b.js
@Service()
class AnotherExampleService {}
// index.js
async main() {
  const matchedFiles = glob('services/*.js');
  for (const file of matchedFiles) {
    await import(file);
  }

  launchApp();
}

main();

Since there can be a lot of services (or for example GraphQL-Resolvers or Chatbot-Commands, etc.) in a project, it can be tedious to register them all by hand, having massive barrel exports of dozens or even hundred classes, just to register them to some library.

I am using a setup like the above to automatically load such files marked with a decorator and doing the registering logic inside the decorator. However since those classes are never actually imported anywhere statically, they are marked as unused which causes a bunch of errors.

Copy link
Copy Markdown
Contributor

@dyc3 dyc3 Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's much more clear, thank you. Yeah, our semantic model isn't quite smart enough to pick that up.

It feels like to me it would make more sense add configurable filters based on the syntax construct (for lack of a better word) that is being defined.

Roughly speaking, you would configure the rule something like this:

{
  "noUnusedVariables": {
    "options": {
       "ignore": {
         "*": ["foo"], // all syntax constructs
         "class": ["Bar"], // only classes
         "function": ["*"], // ignore all functions
         "variable": [], // only variables, but empty so no-op
         // etc...
       }
    }
  }
}

where * is a hard coded special value to mean "everything", and the lists of identifiers are just strings and not regexes. I'm particularly wary of adding RestrictedRegex to this rule because it's one of the rules that gets run the most often on every codebase.

And then, for your specific use case ignoring all classes in those files, you could use noExcessiveClassesPerFile to make sure you don't add extra classes to those files, which would otherwise become false negatives.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, sounds like a plan! Just to confirm: The strings are to be interpreted as equality matches, correct? Not prefix, not suffix and not everything containing those strings?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

Copy link
Copy Markdown
Member

@ematipico ematipico Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm particularly wary of adding RestrictedRegex to this rule because it's one of the rules that gets run the most often on every codebase.

Sure, it makes sense, however, it's not like we're adding regex by default. If the user defines an incorrect regex, it's not Biome's fault.

Still, if normal strings are fine for the time being, that's also fine.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the implementation with the same symbol types found in the diagnostic function, i.e. class, function, interface, parameter, type alias, type parameter and variable as well as the special "*" key.

I think retrofitting this with regex should be relatively straightforward if it comes up in the future (with some migration needed for the wildcard, I suppose).

)
});
let is_used_as_reference = embedded_references.is_used_as_value(binding_name);
let is_ignored = ctx.options().ignore_pattern().is_match(binding_name);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I would prefer if this check occurs as soon as possible, at the top of the rule, and to make the rule return early.

///
/// Default: `null` (no variables are excluded)
///
/// #### Example
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
/// #### Example
/// #### Examples

@mvarendorff mvarendorff changed the base branch from main to next March 8, 2026 17:48
@github-actions github-actions bot added A-CLI Area: CLI A-Project Area: project A-Parser Area: parser A-Formatter Area: formatter A-LSP Area: language server protocol L-CSS Language: CSS and super languages L-JSON Language: JSON and super languages A-Diagnostic Area: diagnostocis L-HTML Language: HTML and super languages labels Mar 8, 2026
@mvarendorff mvarendorff force-pushed the feat/no-unused-variables-ignore-pattern branch from 04b98c9 to 5452b27 Compare March 8, 2026 18:04
@github-actions github-actions bot removed A-Project Area: project A-Parser Area: parser A-Formatter Area: formatter A-LSP Area: language server protocol L-CSS Language: CSS and super languages A-Diagnostic Area: diagnostocis L-HTML Language: HTML and super languages labels Mar 8, 2026
@mvarendorff mvarendorff changed the title feat(lint): no unused variables ignore pattern feat(lint): add ignore option to no-unused-variables Mar 14, 2026
@mvarendorff mvarendorff force-pushed the feat/no-unused-variables-ignore-pattern branch from 6584108 to 3873de5 Compare March 14, 2026 18:59
@mvarendorff mvarendorff requested a review from dyc3 March 14, 2026 19:05
@mvarendorff mvarendorff marked this pull request as ready for review March 14, 2026 19:05
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an ignore mechanism to the noUnusedVariables rule and its options. Introduces a new public NoUnusedVariablesOptionsIgnore type (fields for */all, class, function, interface, type_alias, type_parameter, variable), exposes ignore on NoUnusedVariablesOptions, and adds a public is_ignored(binding, options) helper that short‑circuits unused‑variable checks. Integrates docs and tests covering valid ignores, wildcard patterns, and invalid ignore scenarios. No other behavioural changes introduced.

Possibly related PRs

Suggested labels

A-Diagnostic

Suggested reviewers

  • dyc3
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding an ignore option to the noUnusedVariables rule.
Description check ✅ Passed The description explains the feature's purpose, rationale, and use-case; it's well-aligned with the changeset which implements ignore patterns across rule options and test files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can generate walkthrough in a markdown collapsible section to save space.

Enable the reviews.collapse_walkthrough setting to generate walkthrough in a markdown collapsible section.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs`:
- Around line 531-539: The current selection of specific_ignores uses the
binding's immediate parent kind which misclassifies destructured/rest
parameters; update the logic that computes specific_ignores (the match on
binding.syntax().parent()?.kind()) to walk up the ancestor chain from
binding.syntax() until you find the declaring node (e.g., a parameter list,
function declaration, class declaration, TS interface/type declarations) and
then match that ancestor's kind to choose ignore_options.parameter, .function,
.class, .interface, .type_alias, .type_parameter, or .variable accordingly so
destructured and rest parameters are classified as parameters rather than
variables.

In `@crates/biome_rule_options/src/no_unused_variables.rs`:
- Around line 26-27: Update the rustdoc so it no longer references the removed
`ignore_pattern` or "returns a regex" behavior: change the doc comment for the
`ignore()` method (in the no_unused_variables module / `NoUnusedVariables` type)
to describe that `ignore()` now returns structured ignore groups (the new
structured type) and explain how those groups are interpreted, instead of saying
it "returns a regex" or "doesn't match anything"; keep the description concise
and mention the returned structure name so readers can find the type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 311da800-3c27-490c-8b5a-43b437361a1c

📥 Commits

Reviewing files that changed from the base of the PR and between 8574432 and 3873de5.

⛔ Files ignored due to path filters (5)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidIgnoreAllPattern.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidIgnoreClassPattern.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidIgnoreWildcardPattern.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredPattern.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (11)
  • .changeset/shaggy-grapes-act.md
  • crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidIgnoreAllPattern.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidIgnoreAllPattern.options.json
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidIgnoreClassPattern.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidIgnoreClassPattern.options.json
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidIgnoreWildcardPattern.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidIgnoreWildcardPattern.options.json
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredPattern.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredPattern.options.json
  • crates/biome_rule_options/src/no_unused_variables.rs

Comment thread crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs
Comment thread crates/biome_rule_options/src/no_unused_variables.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/biome_rule_options/src/no_unused_variables.rs (1)

28-30: Consider using unwrap_or_default() for brevity.

Since NoUnusedVariablesOptionsIgnore derives Default, this can be simplified.

✨ Suggested simplification
     pub fn ignore(&self) -> NoUnusedVariablesOptionsIgnore {
-        self.ignore.clone().unwrap_or(NoUnusedVariablesOptionsIgnore::default())
+        self.ignore.clone().unwrap_or_default()
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_rule_options/src/no_unused_variables.rs` around lines 28 - 30,
The method NoUnusedVariablesOptions::ignore currently calls
self.ignore.clone().unwrap_or(NoUnusedVariablesOptionsIgnore::default());
simplify it by using unwrap_or_default() on the cloned Option to leverage the
Default impl of NoUnusedVariablesOptionsIgnore (e.g., replace
self.ignore.clone().unwrap_or(NoUnusedVariablesOptionsIgnore::default()) with
self.ignore.clone().unwrap_or_default()) to make the code shorter and clearer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_rule_options/src/no_unused_variables.rs`:
- Around line 28-30: The method NoUnusedVariablesOptions::ignore currently calls
self.ignore.clone().unwrap_or(NoUnusedVariablesOptionsIgnore::default());
simplify it by using unwrap_or_default() on the cloned Option to leverage the
Default impl of NoUnusedVariablesOptionsIgnore (e.g., replace
self.ignore.clone().unwrap_or(NoUnusedVariablesOptionsIgnore::default()) with
self.ignore.clone().unwrap_or_default()) to make the code shorter and clearer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3c4d3aca-061c-48af-83a6-7067473d6a33

📥 Commits

Reviewing files that changed from the base of the PR and between 3873de5 and c657be5.

📒 Files selected for processing (1)
  • crates/biome_rule_options/src/no_unused_variables.rs

Copy link
Copy Markdown
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job! The documentation may require some polishing

///
/// Default: `{}` (no variables are excluded)
///
/// #### Examples
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have this header, it's not needed

Alternatively, add multiple examples, and the header becomes the name of the example

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was taking the structure from the existing option: https://biomejs.dev/linter/rules/no-unused-variables/#ignorerestsiblings. To have a reference for how it should look like instead with multiple examples - is this desirable: https://biomejs.dev/assist/actions/use-sorted-attributes/#sortorder ? Or should everything including options just go into the Examples section?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah some docs are old and don't follow the new guidelines.

If you plan having multiple explanations, you could follow the organize imports lead, otherwise we don't need the additional header IMHO

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shuffled a few things around and wrote a couple sentences for the new option. I also dropped an additional comment on the ignoreRestSiblings example for good measure. The other review comments should be addressed as well now!

Comment thread crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs
}
}

if is_ignored(binding, ctx.options()).unwrap_or(false) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if is_ignored(binding, ctx.options()).unwrap_or(false) {
if is_ignored(binding, ctx.options()).unwrap_or_default() {

In rust it's idiomatic to use the default like this.

}
}

/// Returns `true` if `binding` is considered as ignored.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Returns `true` if `binding` is considered as ignored.
/// Returns `true` if `binding` is considered as ignored by the user.

@mvarendorff mvarendorff force-pushed the feat/no-unused-variables-ignore-pattern branch from 3862a07 to f1bf13e Compare March 15, 2026 13:55
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs (1)

534-542: ⚠️ Potential issue | 🟠 Major

ignore.parameter is still misclassified for destructured/rest params.

At Line 534, matching on binding.syntax().parent()?.kind() uses the immediate parent, so bindings like function f({ ignored }) {} or function f(...ignored) {} can fall through to variable instead of parameter. That makes ignore.parameter unreliable for common parameter forms.

Suggested fix
-    let specific_ignores = match binding.syntax().parent()?.kind() {
-        JsSyntaxKind::JS_FORMAL_PARAMETER => ignore_options.parameter,
-        JsSyntaxKind::JS_FUNCTION_DECLARATION => ignore_options.function,
-        JsSyntaxKind::JS_CLASS_DECLARATION => ignore_options.class,
-        JsSyntaxKind::TS_INTERFACE_DECLARATION => ignore_options.interface,
-        JsSyntaxKind::TS_TYPE_ALIAS_DECLARATION => ignore_options.type_alias,
-        JsSyntaxKind::TS_TYPE_PARAMETER => ignore_options.type_parameter,
-        _ => ignore_options.variable,
-    };
+    let declaration = binding.declaration()?;
+    let declaration = declaration
+        .parent_binding_pattern_declaration()
+        .unwrap_or(declaration);
+
+    let specific_ignores = match declaration {
+        AnyJsBindingDeclaration::JsFormalParameter(_)
+        | AnyJsBindingDeclaration::JsRestParameter(_)
+        | AnyJsBindingDeclaration::TsIndexSignatureParameter(_)
+        | AnyJsBindingDeclaration::TsPropertyParameter(_) => ignore_options.parameter,
+        AnyJsBindingDeclaration::JsFunctionDeclaration(_) => ignore_options.function,
+        AnyJsBindingDeclaration::JsClassDeclaration(_) => ignore_options.class,
+        AnyJsBindingDeclaration::TsInterfaceDeclaration(_) => ignore_options.interface,
+        AnyJsBindingDeclaration::TsTypeAliasDeclaration(_) => ignore_options.type_alias,
+        AnyJsBindingDeclaration::TsTypeParameter(_) | AnyJsBindingDeclaration::TsInferType(_) => {
+            ignore_options.type_parameter
+        }
+        _ => ignore_options.variable,
+    };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs` around
lines 534 - 542, specific_ignores misclassifies destructured/rest parameters
because the code uses binding.syntax().parent()?.kind() to detect parameter
context; instead, walk ancestors from binding.syntax() to find an enclosing
parameter-like node (e.g., JS_FORMAL_PARAMETER, REST_PARAMETER, and any
pattern/parameter wrapper kinds your AST provides) and base the match on that
ancestor’s kind so ignore_options.parameter is chosen for destructured and rest
params; update the match that sets specific_ignores (currently using
binding.syntax().parent()?.kind()) to inspect the nearest ancestor parameter
node before falling back to variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs`:
- Around line 534-542: specific_ignores misclassifies destructured/rest
parameters because the code uses binding.syntax().parent()?.kind() to detect
parameter context; instead, walk ancestors from binding.syntax() to find an
enclosing parameter-like node (e.g., JS_FORMAL_PARAMETER, REST_PARAMETER, and
any pattern/parameter wrapper kinds your AST provides) and base the match on
that ancestor’s kind so ignore_options.parameter is chosen for destructured and
rest params; update the match that sets specific_ignores (currently using
binding.syntax().parent()?.kind()) to inspect the nearest ancestor parameter
node before falling back to variable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9083317d-5aab-4b88-af28-bfff75f43f27

📥 Commits

Reviewing files that changed from the base of the PR and between a1af182 and f1bf13e.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs

@mvarendorff mvarendorff requested a review from ematipico March 15, 2026 14:21
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs (1)

521-545: Implementation looks solid and aligns with the diagnostic function's pattern.

The match on binding.syntax().parent()?.kind() correctly mirrors the symbol_type detection in diagnostic, ensuring consistent behaviour between ignore matching and diagnostic messages.

One optional readability nit: lines 527 and 542 are rather long. Breaking them up could improve scannability.

,

♻️ Optional: break long lines for readability
-    let is_all_ignored = ignore_options.all.unwrap_or_default().iter().any(|ignore| &**ignore == NoUnusedVariablesOptionsIgnore::IGNORE_ALL || &**ignore == binding_name);
+    let is_all_ignored = ignore_options
+        .all
+        .unwrap_or_default()
+        .iter()
+        .any(|ignore| {
+            &**ignore == NoUnusedVariablesOptionsIgnore::IGNORE_ALL || &**ignore == binding_name
+        });
-    let is_specific_ignored = specific_ignores.unwrap_or_default().iter().any(|ignore| &**ignore == NoUnusedVariablesOptionsIgnore::IGNORE_ALL || &**ignore == binding_name);
+    let is_specific_ignored = specific_ignores
+        .unwrap_or_default()
+        .iter()
+        .any(|ignore| {
+            &**ignore == NoUnusedVariablesOptionsIgnore::IGNORE_ALL || &**ignore == binding_name
+        });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs` around
lines 521 - 545, The two long iterator lines in the is_ignored function (where
is_all_ignored and is_specific_ignored are computed) should be split for
readability: extract the comparison predicate into a small closure or local
function (e.g., let matches_ignore = |ignore: &String| -> bool { &**ignore ==
NoUnusedVariablesOptionsIgnore::IGNORE_ALL || &**ignore == binding_name };) and
then replace the long iter.any(...) calls with iter().any(matches_ignore) (apply
this to both the is_all_ignored and specific_ignores checks, referencing
binding_name, options.ignore(), NoUnusedVariablesOptionsIgnore::IGNORE_ALL,
specific_ignores, and is_specific_ignored to locate the spots).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs`:
- Around line 521-545: The two long iterator lines in the is_ignored function
(where is_all_ignored and is_specific_ignored are computed) should be split for
readability: extract the comparison predicate into a small closure or local
function (e.g., let matches_ignore = |ignore: &String| -> bool { &**ignore ==
NoUnusedVariablesOptionsIgnore::IGNORE_ALL || &**ignore == binding_name };) and
then replace the long iter.any(...) calls with iter().any(matches_ignore) (apply
this to both the is_all_ignored and specific_ignores checks, referencing
binding_name, options.ignore(), NoUnusedVariablesOptionsIgnore::IGNORE_ALL,
specific_ignores, and is_specific_ignored to locate the spots).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 94475733-2c2c-467f-a447-5ebdf5d33a1b

📥 Commits

Reviewing files that changed from the base of the PR and between f1bf13e and a5121c0.

⛔ Files ignored due to path filters (1)
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (2)
  • crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs
  • crates/biome_rule_options/src/no_unused_variables.rs

Copy link
Copy Markdown
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some suggestions for the docs and the changeset

Comment thread .changeset/shaggy-grapes-act.md Outdated
"@biomejs/biome": minor
---

Added `ignore` option to the [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) rule. The option allows excluding identifiers by providing a list of ignored names.
Copy link
Copy Markdown
Member

@ematipico ematipico Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mvarendorff can you please provide a more descriptive changeset? We're adding a new feature, so would want to show it off a bit more. For example we should explain what it solves, and provide a real world example.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope this is more appropriate? If not, I am open to suggestions - writing changelogs is not really my forte 😄

///
/// Default: `{}` (no variables are excluded)
///
/// For example, we can exclude all unused identifiers named `ignored` regardless of their kind,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// For example, we can exclude all unused identifiers named `ignored` regardless of their kind,
/// For example, you can exclude all unused identifiers named `ignored` regardless of their kind,

You should either use the third person "it" (e.g. Biome excludes) or "you" (e.g. you can exclude).

/// "ignore": {
/// "*": ["ignored"],
/// "class": ["IgnoredClass"],
/// "function": ["*"],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The * as a value was never explained in the docs until now. We explained as a key though. We should address that. Plus, the description to the example doesn't mention function being used and the effects it has

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs already contained a note "The special identifier "*" can be used to match all identifiers in the respective group.". I rephrased it to indicate both usages as key and as value more clearly now (hopefully).

Comment thread crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs
Comment thread crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs
@mvarendorff mvarendorff requested a review from ematipico March 18, 2026 17:18
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 18, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 156 skipped benchmarks1


Comparing mvarendorff:feat/no-unused-variables-ignore-pattern (eeceada) with next (74b20ee)

Open in CodSpeed

Footnotes

  1. 156 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@ematipico
Copy link
Copy Markdown
Member

@mvarendorff as you can see the CI is failing. Use just lint-rules locally to reproduce the issue.

I believe the problem is that the new code block you added, emits more than one diagnostic (by design, a code block must emit only one diagnostic). You can add the ignore to the code block , and Biome won't try to analyse it

@mvarendorff mvarendorff force-pushed the feat/no-unused-variables-ignore-pattern branch from e844899 to eeceada Compare March 19, 2026 17:42
@mvarendorff
Copy link
Copy Markdown
Author

I believe the problem is that the new code block you added, emits more than one diagnostic

Yup, that's the one, thank you! I saw the CI failure last night but skipped over the line that explained it. Added the ignore and now everything looks fine locally 🤞

@ematipico
Copy link
Copy Markdown
Member

@dyc3 please give your review once you have the chance

@dyc3 dyc3 merged commit d5913c9 into biomejs:next Mar 20, 2026
17 checks passed
@mvarendorff mvarendorff deleted the feat/no-unused-variables-ignore-pattern branch March 20, 2026 19:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CLI Area: CLI A-Linter Area: linter L-JavaScript Language: JavaScript and super languages L-JSON Language: JSON and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants