Skip to content

fix(lint): detect array index in any position of template string#8947

Open
murataslan1 wants to merge 2 commits intobiomejs:mainfrom
murataslan1:fix/8812-no-array-index-key-template
Open

fix(lint): detect array index in any position of template string#8947
murataslan1 wants to merge 2 commits intobiomejs:mainfrom
murataslan1:fix/8812-no-array-index-key-template

Conversation

@murataslan1
Copy link

Fixes #8812

Problem

The noArrayIndexKey rule was not detecting array index usage when the index appeared before other expressions in a template string.

Before:

// Correctly flagged
<div key={`${item}-${index}`}>{item}</div>

// NOT flagged (false negative) ❌
<div key={`${index}-${item}`}>{item}</div>

Root Cause

The loop that processes template elements was overwriting capture_array_index for each element. If the last element wasn't a simple identifier expression (e.g., it was an object property access like item.title), the previously captured index was lost.

Solution

Stop searching after finding the first identifier expression in the template string. Once we find a potential array index, we use it and break out of the loop.

Testing

Added test cases for:

  • ${index}-${item} - index at start
  • ${index}-${item.title} - index followed by object property access

Made with Cursor

Fixes biomejs#8812

The `noArrayIndexKey` rule was not detecting array index usage when
the index appeared before other expressions in a template string.

Before:
- `${item}-${index}` - correctly flagged
- `${index}-${item}` - NOT flagged (false negative)

The issue was that the loop overwrote `capture_array_index` for each
template element. If the last element wasn't an identifier expression
(e.g., an object property access), the captured index was lost.

Fix: Stop searching after finding the first identifier expression
in the template string, which is the potential array index.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copilot AI review requested due to automatic review settings February 2, 2026 12:27
@changeset-bot
Copy link

changeset-bot bot commented Feb 2, 2026

🦋 Changeset detected

Latest commit: 9f3a509

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 Feb 2, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a false negative in lint/suspicious/noArrayIndexKey when an array index is used inside a template literal key but not in the last ${...} expression.

Changes:

  • Updates template-literal scanning logic in noArrayIndexKey to avoid losing a previously captured identifier when later template expressions aren’t simple identifiers.
  • Adds invalid test cases for key={${index}-${item}} and key={${index}-${item.title}}.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
crates/biome_js_analyze/src/lint/suspicious/no_array_index_key.rs Adjusts how identifier expressions are extracted from template literals for key analysis.
crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx Adds regression tests for index usage at the start of a template string and before property access.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 2, 2026

Walkthrough

Updates the lint rule that detects use of array indices in React keys inside template literals: the template-element handling now explicitly captures the first identifier found and breaks the search, ensuring index usage is detected regardless of its position in the template. Adds three invalid test components covering reordered index/value positions in template keys. Adds a patch changelog entry documenting the fix to noArrayIndexKey.

Suggested labels

A-Diagnostic

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main change: detecting array index in any position of template strings, directly addressing issue #8812.
Description check ✅ Passed The description clearly explains the problem, root cause, solution, and testing approach related to the noArrayIndexKey lint rule fix.
Linked Issues check ✅ Passed The PR fully addresses issue #8812 by fixing the false negative when array index appears before other expressions in template strings [#8812].
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the noArrayIndexKey rule: implementation fix, test cases, and changelog entry—nothing extraneous.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

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

🤖 Fix all issues with AI agents
In `@crates/biome_js_analyze/src/lint/suspicious/no_array_index_key.rs`:
- Around line 153-162: The current logic breaks early on the first identifier
expression in a template (the block using
template_element.expression().ok()?.as_js_identifier_expression() and setting
capture_array_index), which causes templates like `${item}-${index}` to capture
the wrong identifier; instead, collect all identifier expressions from the
template (or continue scanning instead of breaking) and then choose the
identifier that satisfies is_array_method_index, or iterate candidates calling
is_array_method_index until one returns true before assigning
capture_array_index; update the loop around template_element/identifier_expr to
accumulate candidates and only set capture_array_index when
is_array_method_index confirms the identifier is the array index.
🧹 Nitpick comments (1)
crates/biome_js_analyze/tests/specs/suspicious/noArrayIndexKey/invalid.jsx (1)

136-144: Add a regression case for value‑first template keys

Nice additions. To keep the fix order‑agnostic, consider adding a case like ${item}-${index} (or ${item.title}-${index}) so a future change doesn’t quietly reintroduce order sensitivity.

➕ Suggested test snippet
+// Issue `#8812`: value-first order should still trigger
+function Component13() {
+    return things.map((item, index) => <div key={`${item}-${index}`}>{item}</div>);
+}

Comment on lines +153 to +162
// Check if this element is an identifier expression (potential array index)
if let Some(identifier_expr) =
template_element.expression().ok()?.as_js_identifier_expression()
{
if let Ok(name) = identifier_expr.name() {
// Found a potential array index reference, use it and stop searching
// This fixes the issue where index position in template doesn't matter
capture_array_index = Some(name);
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Potential regression: template literal now ignores later identifiers

Lines 153-162: breaking on the first identifier means ${item}-${index} will capture item and miss index, so order dependence creeps back in. Please consider collecting all identifier expressions in the template and selecting the one that resolves to the array index parameter (or iterating candidates until one passes is_array_method_index).

🤖 Prompt for AI Agents
In `@crates/biome_js_analyze/src/lint/suspicious/no_array_index_key.rs` around
lines 153 - 162, The current logic breaks early on the first identifier
expression in a template (the block using
template_element.expression().ok()?.as_js_identifier_expression() and setting
capture_array_index), which causes templates like `${item}-${index}` to capture
the wrong identifier; instead, collect all identifier expressions from the
template (or continue scanning instead of breaking) and then choose the
identifier that satisfies is_array_method_index, or iterate candidates calling
is_array_method_index until one returns true before assigning
capture_array_index; update the loop around template_element/identifier_expr to
accumulate candidates and only set capture_array_index when
is_array_method_index confirms the identifier is the array index.

- Add changeset for noArrayIndexKey template string fix
- Add test case for value-first order (${item}-${index})

Co-authored-by: Cursor <cursoragent@cursor.com>
@dyc3
Copy link
Contributor

dyc3 commented Feb 2, 2026

@murataslan1 Please don't ask copilot for a review. It doesn't have context about our repo like coderabbit does, and it just makes the PR harder to navigate.

{
if let Ok(name) = identifier_expr.name() {
// Found a potential array index reference, use it and stop searching
// This fixes the issue where index position in template doesn't matter
Copy link
Contributor

Choose a reason for hiding this comment

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

nit

Suggested change
// This fixes the issue where index position in template doesn't matter

Copy link
Contributor

Choose a reason for hiding this comment

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

snapshots need to be updated

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/suspicious/noArrayIndexKey false negative when re-ordering value and index in template string

2 participants