Skip to content

Conversation

@ritoban23
Copy link
Contributor

Summary

The useMaxParams rule was highlighting entire function bodies instead of just the function names, causing noisy error squiggles that made it difficult to quickly identify which function was problematic.

Fixes #7604

Changes:

  • Add name_range() method to AnyFunctionLike union to extract function name positions
  • Update diagnostic to use function name ranges instead of full node ranges
  • Handle all function types including TypeScript declarations and type aliases
  • Fall back to full node range only for truly anonymous functions (where no name exists)
  • Update test snapshots to reflect new precise highlighting behavior

Test Plan

  • All existing tests pass (6/6 useMaxParams tests)
  • Updated snapshots demonstrate the improved highlighting behavior:
    • Named functions: function myFunc(...) → highlights only myFunc
    • Class methods: method(...) → highlights only method
    • Constructors: constructor(...) → highlights only constructor
    • TypeScript declarations: declare function makeDate(...) → highlights only makeDate
    • Anonymous functions: function(...) → highlights entire function (correct fallback)
    • Arrow functions: (...) => ... → highlights entire function (correct fallback)
  • Ran full validation pipeline: formatting, linting, and code generation all pass

Docs

@changeset-bot
Copy link

changeset-bot bot commented Sep 28, 2025

🦋 Changeset detected

Latest commit: e729c6b

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

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

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 Sep 28, 2025
Copy link
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.

Have you tried using the range of the parameters? It should be possible to create a range from the node list.

Regardless, a changeset is missing and it's required

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 28, 2025

Walkthrough

Added a TextRange import and implemented AnyFunctionLike::parameter_range() to compute the textual range of a function-like entity’s parameter list across multiple AST variants (JsFunctionDeclaration, JsFunctionExpression, JsArrowFunctionExpression, JsMethodClassMember, JsMethodObjectMember, JsConstructorClassMember, TsDeclareFunctionDeclaration, and TsTypeAliasDeclaration when it contains a TsFunctionType). The lint now prefers this parameter range and falls back to the node range if absent; emitted diagnostics use the parameter-based range instead of node.range().

Possibly related PRs

Suggested labels

A-Diagnostic

Suggested reviewers

  • ematipico
  • dyc3

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The PR title states "only highlight function names" but the actual implementation highlights the parameter list range, not the function name. The changeset and issue comments confirm that the goal was to highlight parameters (e.g., "(a, b, c, d, e, f, g, h)"), not function names. The title misrepresents the primary change in the code, which adds a parameter_range() method and uses it for diagnostics. Update the title to accurately reflect that the rule now highlights the parameter list, not the function name. For example: "fix(lint): highlight only parameter lists in useMaxParams rule" or "fix(lint): improve diagnostic range for useMaxParams to target parameters".
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues Check ✅ Passed The code changes successfully address the core requirement from issue #7604 by restricting the diagnostic range from the entire function body to a more confined span. The implementation adds a parameter_range() method to compute the textual range of parameters and updates diagnostics to use this range, thereby reducing visual noise. This covers functions, methods, TypeScript declarations, and type aliases as requested, with appropriate fallback behaviour for anonymous cases.
Out of Scope Changes Check ✅ Passed All changes in the PR are directly related to improving the diagnostic range for the useMaxParams rule as specified in issue #7604. The parameter_range() method implementation, diagnostic updates, and changeset documentation all serve the single objective of highlighting a more precise source span (parameters) rather than entire function bodies. No unrelated refactoring, feature additions, or out-of-scope modifications are present.
Description Check ✅ Passed The PR description is related to the changeset as it discusses improving the useMaxParams rule's highlighting behaviour. However, it contains a significant factual error: it claims to add a name_range() method and highlight function names, but the code actually implements parameter_range() to highlight parameter lists. Despite this inaccuracy, the description does address the core objective of improving diagnostic precision for the useMaxParams rule.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffcd20b and e729c6b.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs

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

Tip

🧪 Early access (models): enabled

We are currently testing Sonnet 4.5 code review models, which should lead to better review quality. However, this model may result in higher noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

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

Copy link
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: 1

🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/nursery/use_max_params.rs (2)

211-213: Diagnostic now points to the name — good; tiny micro‑nit

Using the name range markedly improves UX. Micro‑nit: TextRange is Copy, so unwrap_or(node.range()) would avoid the closure. Not worth a separate commit unless you touch this again.

Also applies to: 217-217


103-106: Arrow functions — optional: highlight binding/property name instead of full node

Currently Self::JsArrowFunctionExpression(_) returns None and falls back to a full-node squiggle; consider resolving the containing declarator or property key (e.g. const foo = () => {} or { foo: () => {} }) and pointing the diagnostic at that identifier, keeping the current behaviour for inline callbacks.

File: crates/biome_js_analyze/src/lint/nursery/use_max_params.rs (lines 103–106)

            Self::JsArrowFunctionExpression(_) => {
                // Arrow functions don't have names
                None
            }

Ripgrep of the test suite shows many arrow-in-declarator cases (eg. crates/biome_grit_patterns/tests/specs/ts/treeSitterCompatibility.ts, crates/biome_js_parser/tests/js_test_suite/ok/jsx_element_on_arrow_function.jsx) — update snapshots if you change diagnostic ranges.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c294644 and b23460f.

⛔ Files ignored due to path filters (3)
  • crates/biome_js_analyze/tests/specs/nursery/useMaxParams/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useMaxParams/invalid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useMaxParams/invalidCustomMax.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs
🧠 Learnings (1)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : Avoid deep indentation and panics; prefer `?`, `ok()?`, and combinators (`map`, `and_then`, `filter`) over nested `if let`/`unwrap`/`expect`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs
🔇 Additional comments (3)
crates/biome_js_analyze/src/lint/nursery/use_max_params.rs (3)

11-11: Import change looks good

Brings TextRange and declare_node_union into scope; required for the new API and macro usage.


87-137: Solid, panic‑free traversal of AST for name ranges

Nice use of ok()?/? to avoid unwraps and deep nesting; covers decls, exprs, methods, constructors, TS declare fn and type aliases. This aligns with our guidance to avoid panics.

Based on learnings


109-111: Constructor naming: double‑check the highlighted token

constructor.name() should return the range for the 'constructor' keyword/token — confirm the snapshot underlines only that token, not the whole header.

Location: crates/biome_js_analyze/src/lint/nursery/use_max_params.rs:109–111

Comment on lines 97 to 102
Self::JsFunctionExpression(func) => func
.id()?
.as_js_identifier_binding()?
.name_token()
.ok()
.map(|token| token.text_range()),
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Consider preferring the binding name over the internal name for function expressions

When a named function expression is assigned (const foo = function bar(...) {}), highlighting bar is less helpful than foo. Optional follow‑up: if id() is None or even when present, try resolving the enclosing JsVariableDeclarator/property name to highlight the LHS binding/property key instead. This would bring function expressions in line with the “underline what the developer sees” principle.

If you want to gauge impact, search snapshots for these cases:


🏁 Script executed:

#!/bin/bash
rg -n -C2 -g 'crates/**/tests/**' -e $'const\\s+\\w+\\s*=\\s*function\\b' -e $'\\w+\\s*:\\s*function\\b'

Length of output: 202398


Prefer binding name over internal identifier in use_max_params lint
• Resolve the enclosing variable/property binding (e.g. fn1/fn2) and use its text range instead of func.id() for both anonymous and named function expressions.
• Update snapshots in crates/biome_js_analyze/tests/specs/nursery/useMaxParams/invalid.js.snap (around lines 95 and 117) to underline fn1/fn2 rather than the parameter list or internal name.

🤖 Prompt for AI Agents
In crates/biome_js_analyze/src/lint/nursery/use_max_params.rs around lines 97 to
102, the code uses func.id() to get the internal function identifier text range;
instead, resolve the enclosing binding (variable or property) that holds the
function expression and return that binding's text_range for both anonymous and
named function expressions so the diagnostic highlights the external name (e.g.,
fn1/fn2) rather than the internal id or the parameter list. Modify the logic to
walk to the parent binding (variable declarator or property) and extract its
identifier/token range when present, falling back to the existing func.id()
behavior only if no enclosing binding exists. After changing the lint, update
the snapshots in
crates/biome_js_analyze/tests/specs/nursery/useMaxParams/invalid.js.snap (around
lines ~95 and ~117) to underline the external binding names fn1/fn2 instead of
the parameter list or internal name.

@ritoban23
Copy link
Contributor Author

Have you tried using the range of the parameters? It should be possible to create a range from the node list.

I can try that out..would you like me to update the implementation that way

@ematipico
Copy link
Member

Yes. And please update the changeset by following our guidelines

Copy link
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: 0

🧹 Nitpick comments (1)
crates/biome_js_analyze/src/lint/nursery/use_max_params.rs (1)

207-209: Align diagnostic range with “prefer name, then params, else node”

At the moment, diagnostics always favour the parameter list. Switch to name-first to match the PR title and issue intent.

-        // Use the parameter list range if available, otherwise fall back to the whole node
-        let range = node.parameter_range().unwrap_or_else(|| node.range());
+        // Prefer the visible name; then parameters; finally the whole node
+        let range = node
+            .name_range()
+            .or_else(|| node.parameter_range())
+            .unwrap_or_else(|| node.range());

If you intentionally chose parameters (per discussion with maintainers), please update the PR title/description to avoid surprises in release notes.

Also applies to: 213-213

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e85beec and 9251cec.

⛔ Files ignored due to path filters (3)
  • crates/biome_js_analyze/tests/specs/nursery/useMaxParams/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useMaxParams/invalid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useMaxParams/invalidCustomMax.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • .changeset/ten-donkeys-raise.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/ten-donkeys-raise.md
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs
🧠 Learnings (2)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : Avoid deep indentation and panics; prefer `?`, `ok()?`, and combinators (`map`, `and_then`, `filter`) over nested `if let`/`unwrap`/`expect`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_max_params.rs
🔇 Additional comments (2)
crates/biome_js_analyze/src/lint/nursery/use_max_params.rs (2)

11-11: Import looks good

TextRange is used by the new helpers; the import set is tidy.


87-133: Prefer highlighting the visible name (binding/method/alias) with params as fallback

Current impl returns the parameter list range for all variants. That’s an improvement, but it doesn’t match the PR title (“only highlight function names”) nor the linked issue’s “prefer the function name”. It also misses the common case of function/arrow expressions where the user-visible name is the LHS binding (e.g. const foo = (...) => {}).

Suggestion: add a lightweight name_range() that:

  • Decls: underline id.
  • Methods: underline the method/property name; constructors: the constructor keyword.
  • Function/arrow expressions: prefer enclosing variable/property name; fall back to internal id (if any), then params.
  • TS: declare function uses id; type Fn = (…)=>… underlines the alias Fn.

Then pick name_range().or(parameter_range()).unwrap_or(node.range()) in diagnostics.

Minimal diff sketch:

 impl AnyFunctionLike {
+    pub fn name_range(&self) -> Option<TextRange> {
+        match self {
+            Self::JsFunctionDeclaration(func) => func.id().ok().map(|id| id.range()),
+            Self::JsMethodClassMember(m) => m.name().ok().map(|n| n.range()),
+            Self::JsMethodObjectMember(m) => m.name().ok().map(|n| n.range()),
+            Self::JsConstructorClassMember(c) => Some(c.range()), // or the `constructor` token if available
+            Self::TsDeclareFunctionDeclaration(d) => d.id().ok().map(|id| id.range()),
+            Self::TsTypeAliasDeclaration(d) => d.id().ok().map(|id| id.range()),
+            // For expressions and arrows, try the enclosing binding/property first
+            Self::JsFunctionExpression(func) => enclosing_binding_or_property_name_range(func.syntax()),
+            Self::JsArrowFunctionExpression(func) => enclosing_binding_or_property_name_range(func.syntax()),
+        }
+    }
+
     pub fn parameter_range(&self) -> Option<TextRange> {
         match self {
             Self::JsFunctionDeclaration(func) => func
                 .parameters()
                 .ok()
                 .map(|params| params.range()),
             /* …unchanged for the other variants… */
         }
     }
 }
+
+fn enclosing_binding_or_property_name_range(node: &biome_rowan::SyntaxNode) -> Option<TextRange> {
+    use biome_js_syntax::{JsVariableDeclarator, JsPropertyObjectMember};
+    let mut cur = Some(node.clone());
+    while let Some(n) = cur {
+        if let Some(decl) = JsVariableDeclarator::cast(n.clone()) {
+            return decl.id().ok().map(|id| id.range());
+        }
+        if let Some(prop) = JsPropertyObjectMember::cast(n.clone()) {
+            return prop.name().ok().map(|name| name.range());
+        }
+        cur = n.parent();
+    }
+    None
+}

This keeps squiggles compact and matches user expectations across forms.

The useMaxParams rule was highlighting entire function bodies instead
of just the function names, causing noisy error squiggles.

Changes:
- Add name_range() method to AnyFunctionLike union
- Update diagnostic to use function name ranges
- Handle all function types including TS declarations
- Update test snapshots to reflect new behavior
@ritoban23 ritoban23 force-pushed the fix/use-max-params-highlight branch from 601e9b4 to ffcd20b Compare September 29, 2025 10:40
@codspeed-hq
Copy link

codspeed-hq bot commented Sep 29, 2025

CodSpeed Performance Report

Merging #7608 will not alter performance

Comparing ritoban23:fix/use-max-params-highlight (e729c6b) with main (2b9b9d8)

Summary

✅ 133 untouched

@ematipico ematipico merged commit 41df59b into biomejs:main Oct 2, 2025
30 checks passed
@github-actions github-actions bot mentioned this pull request Oct 2, 2025
ematipico pushed a commit that referenced this pull request Oct 3, 2025
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💅 lint/nursery/useMaxParams should not put error squiggles over the entire method

2 participants