From 91d7d43ce18629b2bcfe52c8a129ab3121c969f2 Mon Sep 17 00:00:00 2001 From: printfn Date: Fri, 17 May 2024 10:43:55 +0000 Subject: [PATCH] feat(noUnusedVariables): add option to ignore unused function arguments --- CHANGELOG.md | 2 + .../lint/correctness/no_unused_variables.rs | 54 ++++++++++++++++--- .../validIgnoredArguments.options.json | 15 ++++++ .../validIgnoredArguments.ts | 7 +++ .../validIgnoredArguments.ts.snap | 15 ++++++ .../@biomejs/backend-jsonrpc/src/workspace.ts | 26 ++++++++- .../@biomejs/biome/configuration_schema.json | 52 +++++++++++++++++- 7 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.options.json create mode 100644 crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.ts create mode 100644 crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.ts.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 970c052f8e18..9e13e8bf382a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -168,6 +168,8 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b - Add [nursery/useThrowNewError](https://biomejs.dev/linter/rules/use-throw-new-error/). Contributed by @minht11 - Add [nursery/useTopLevelRegex](https://biomejs.dev/linter/rules/use-top-level-regex), which enforces defining regular expressions at the top level of a module. [#2148](https://github.com/biomejs/biome/issues/2148) Contributed by @dyc3. +- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) now supports an option to ignore unused function arguments. + Contributed by @printfn #### Enhancements diff --git a/crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs b/crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs index e48d888a4f51..65a033f20da2 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs @@ -5,6 +5,7 @@ use biome_analyze::{ context::RuleContext, declare_rule, ActionCategory, FixKind, Rule, RuleDiagnostic, }; use biome_console::markup; +use biome_deserialize_macros::Deserializable; use biome_diagnostics::Applicability; use biome_js_semantic::ReferencesExtensions; use biome_js_syntax::binding_ext::{ @@ -17,6 +18,9 @@ use biome_js_syntax::{ TsInferType, }; use biome_rowan::{AstNode, BatchMutationExt, Direction, SyntaxResult}; +#[cfg(feature = "schemars")] +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; declare_rule! { /// Disallow unused variables. @@ -66,7 +70,7 @@ declare_rule! { /// export function f() {} /// ``` /// - /// # Valid + /// ### Valid /// /// ```js /// function foo(b) { @@ -87,6 +91,20 @@ declare_rule! { /// } /// used_overloaded(); /// ``` + /// + /// ## Options + /// + /// ```json + /// { + /// "//": "...", + /// "options": { + /// "args": "all" + /// } + /// } + /// ``` + /// + /// The "args" option can be set to "none" to disable argument checking + /// pub NoUnusedVariables { version: "1.0.0", name: "noUnusedVariables", @@ -100,6 +118,26 @@ declare_rule! { } } +#[derive(Clone, Debug, Default, Deserialize, Deserializable, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct NoUnusedVariablesOptions { + /// Whether to check function arguments + #[serde(default)] + args: ArgsBehavior, +} + +#[derive(Clone, Debug, Default, Deserialize, Deserializable, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase")] +pub enum ArgsBehavior { + /// All named arguments must be used + #[default] + All, + /// Do not check function arguments + None, +} + /// Suggestion if the bindnig is unused #[derive(Debug)] pub enum SuggestedFix { @@ -141,7 +179,10 @@ fn suggestion_for_binding(binding: &AnyJsIdentifierBinding) -> Option Option { +fn suggested_fix_if_unused( + binding: &AnyJsIdentifierBinding, + options: &NoUnusedVariablesOptions, +) -> Option { let decl = binding.declaration()?; // It is fine to ignore unused rest spread siblings if let node @ (AnyJsBindingDeclaration::JsObjectBindingPatternShorthandProperty(_) @@ -176,14 +217,14 @@ fn suggested_fix_if_unused(binding: &AnyJsIdentifierBinding) -> Option None, AnyJsBindingDeclaration::JsFormalParameter(parameter) => { - if is_function_that_is_ok_parameter_not_be_used(¶meter.parent_function()) { + if options.args == ArgsBehavior::None || is_function_that_is_ok_parameter_not_be_used(¶meter.parent_function()) { None } else { suggestion_for_binding(binding) } } AnyJsBindingDeclaration::JsRestParameter(parameter) => { - if is_function_that_is_ok_parameter_not_be_used(¶meter.parent_function()) { + if options.args == ArgsBehavior::None || is_function_that_is_ok_parameter_not_be_used(¶meter.parent_function()) { None } else { suggestion_for_binding(binding) @@ -266,7 +307,7 @@ impl Rule for NoUnusedVariables { type Query = Semantic; type State = SuggestedFix; type Signals = Option; - type Options = (); + type Options = NoUnusedVariablesOptions; fn run(ctx: &RuleContext) -> Option { if ctx @@ -281,6 +322,7 @@ impl Rule for NoUnusedVariables { } let binding = ctx.query(); + let options = ctx.options(); let name = binding.name_token().ok()?; let name = name.text_trimmed(); @@ -295,7 +337,7 @@ impl Rule for NoUnusedVariables { return None; } - let suggestion = suggested_fix_if_unused(binding)?; + let suggestion = suggested_fix_if_unused(binding, options)?; let model = ctx.model(); if model.is_exported(binding) { diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.options.json b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.options.json new file mode 100644 index 000000000000..3f4692355a3b --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.options.json @@ -0,0 +1,15 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "rules": { + "correctness": { + "noUnusedVariables": { + "level": "error", + "options": { + "args": "none" + } + } + } + } + } +} diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.ts b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.ts new file mode 100644 index 000000000000..994787c73966 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.ts @@ -0,0 +1,7 @@ +/* should not generate diagnostics because we're ignoring unused arguments */ + +export function foo(a: string) { + return 5; +} + +export function bar({ a, b = 5 }: { a: number, b?: number }) {} diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.ts.snap b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.ts.snap new file mode 100644 index 000000000000..e034068d82f6 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validIgnoredArguments.ts.snap @@ -0,0 +1,15 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: validIgnoredArguments.ts +--- +# Input +```ts +/* should not generate diagnostics because we're ignoring unused arguments */ + +export function foo(a: string) { + return 5; +} + +export function bar({ a, b = 5 }: { a: number, b?: number }) {} + +``` diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 7365f8f19f05..ecc75360dec6 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -866,7 +866,7 @@ export interface Correctness { /** * Disallow unused variables. */ - noUnusedVariables?: RuleConfiguration_for_Null; + noUnusedVariables?: RuleConfiguration_for_NoUnusedVariablesOptions; /** * This rules prevents void elements (AKA self-closing elements) from having children. */ @@ -1593,6 +1593,9 @@ export type RuleConfiguration_for_ValidAriaRoleOptions = export type RuleConfiguration_for_ComplexityOptions = | RulePlainConfiguration | RuleWithOptions_for_ComplexityOptions; +export type RuleConfiguration_for_NoUnusedVariablesOptions = + | RulePlainConfiguration + | RuleWithOptions_for_NoUnusedVariablesOptions; export type RuleConfiguration_for_HooksOptions = | RulePlainConfiguration | RuleWithOptions_for_HooksOptions; @@ -1663,6 +1666,20 @@ export interface RuleWithOptions_for_ComplexityOptions { */ options: ComplexityOptions; } +export interface RuleWithOptions_for_NoUnusedVariablesOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ + level: RulePlainConfiguration; + /** + * Rule's options + */ + options: NoUnusedVariablesOptions; +} export interface RuleWithOptions_for_HooksOptions { /** * The kind of the code actions emitted by the rule @@ -1806,6 +1823,12 @@ export interface ComplexityOptions { */ maxAllowedComplexity: number; } +export interface NoUnusedVariablesOptions { + /** + * Whether to check function arguments + */ + args?: ArgsBehavior; +} /** * Options for the rule `useExhaustiveDependencies` */ @@ -1891,6 +1914,7 @@ export interface NamingConventionOptions { */ strictCase: boolean; } +export type ArgsBehavior = "all" | "none"; export interface Hook { /** * The "position" of the closure function, starting from zero. diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 96341bc4450d..110c3124abc8 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -297,6 +297,20 @@ }, "additionalProperties": false }, + "ArgsBehavior": { + "oneOf": [ + { + "description": "All named arguments must be used", + "type": "string", + "enum": ["all"] + }, + { + "description": "Do not check function arguments", + "type": "string", + "enum": ["none"] + } + ] + }, "ArrowParentheses": { "type": "string", "enum": ["always", "asNeeded"] }, "AttributePosition": { "type": "string", "enum": ["auto", "multiline"] }, "Complexity": { @@ -790,7 +804,7 @@ "noUnusedVariables": { "description": "Disallow unused variables.", "anyOf": [ - { "$ref": "#/definitions/RuleConfiguration" }, + { "$ref": "#/definitions/NoUnusedVariablesConfiguration" }, { "type": "null" } ] }, @@ -1577,6 +1591,23 @@ "properties": { "allowComments": { "type": "boolean" } }, "additionalProperties": false }, + "NoUnusedVariablesConfiguration": { + "anyOf": [ + { "$ref": "#/definitions/RulePlainConfiguration" }, + { "$ref": "#/definitions/RuleWithNoUnusedVariablesOptions" } + ] + }, + "NoUnusedVariablesOptions": { + "type": "object", + "properties": { + "args": { + "description": "Whether to check function arguments", + "default": "all", + "allOf": [{ "$ref": "#/definitions/ArgsBehavior" }] + } + }, + "additionalProperties": false + }, "Nursery": { "description": "A list of rules that belong to this group", "type": "object", @@ -2250,6 +2281,25 @@ }, "additionalProperties": false }, + "RuleWithNoUnusedVariablesOptions": { + "type": "object", + "required": ["level", "options"], + "properties": { + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/NoUnusedVariablesOptions" }] + } + }, + "additionalProperties": false + }, "RuleWithRestrictedGlobalsOptions": { "type": "object", "required": ["level", "options"],