Skip to content

Conversation

@You-saku
Copy link
Contributor

@You-saku You-saku commented Dec 25, 2025

Summary

Ported the typescript-esilnt rule prefer-string-starts-ends-with to a new Biome lint rule UseStartsEndsWith.

There are multiple ways to verify if a string starts or ends with a specific string, such as foo.indexOf('bar') === 0. As of ES2015, the most common way in JavaScript is to use String#startsWith and String#endsWith. Keeping to those methods consistently helps with code readability.
This rule reports when a string method can be replaced safely with String#startsWith or String#endsWith.

closes #7653

Supplement

I'm not good at Rust lang.
So, I implement this rule with borrowing generative ai(copilot) power.
If code quality is bad, please comment this pr.

@changeset-bot
Copy link

changeset-bot bot commented Dec 25, 2025

⚠️ No Changeset found

Latest commit: 6d494fc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@You-saku You-saku marked this pull request as draft December 25, 2025 16:46
@github-actions github-actions bot added A-Project Area: project A-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Dec 25, 2025
@You-saku You-saku changed the title feat add UseStartsEndsWith rule feat(linter): add UseStartsEndsWith rule Dec 26, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Dec 26, 2025

CodSpeed Performance Report

Merging #8582 will not alter performance

Comparing You-saku:feature-use-starts-ends-with (1c6c747) with main (0196c0e)

Summary

✅ 58 untouched
⏩ 95 skipped1

Footnotes

  1. 95 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.

@You-saku You-saku marked this pull request as ready for review December 26, 2025 15:42
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 26, 2025

Walkthrough

Adds a new nursery lint rule UseStartsEndsWith that detects various string-prefix and -suffix checks and recommends using String.prototype.startsWith or endsWith. The rule inspects binary and call expressions, computed member access, string methods, regex test/match patterns and other idioms (e.g. charAt, indexOf, slice, substring) to extract literal patterns and determine StartsWith/EndsWith variants. New public types and helpers (AnyStringCheckExpression, StartsEndsWithVariant, UseStartsEndsWithState, multiple check_* functions), a Rule implementation, unit-like options, and test files (valid and invalid) were added.

Suggested reviewers

  • dyc3
  • ematipico
  • arendjr
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarises the main change: adding a new linter rule called UseStartsEndsWith.
Description check ✅ Passed The description explains the rule's purpose, references the typescript-eslint source, and links to the related issue #7653.
Linked Issues check ✅ Passed The PR implements issue #7653, porting the typescript-eslint prefer-string-starts-ends-with rule to Biome with support for identifying string-start and string-end checks replaceable by String.prototype methods.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the UseStartsEndsWith rule: new rule implementation, test cases (valid and invalid), and rule options configuration.

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

✨ Finishing touches
  • 📝 Generate docstrings

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: 4

🧹 Nitpick comments (3)
crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/valid.js (1)

1-3: Valid test cases look good, though coverage could be expanded.

These correctly verify that direct startsWith/endsWith usage doesn't trigger diagnostics. Consider adding more edge cases in future iterations, such as:

  • foo.indexOf('bar') === 1 (non-zero index)
  • foo.slice(1, 3) === 'ar' (non-zero start)
  • Dynamic patterns like foo.startsWith(variable)
crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs (2)

83-83: Consider using the options struct instead of unit type.

An options struct UseStartsEndsWithOptions was created in biome_rule_options, but this rule uses type Options = (). Either remove the unused options struct or wire it up here for consistency and future extensibility.


346-379: Regex parsing may produce false positives on complex patterns.

The current implementation extracts the raw pattern and checks for ^/$ anchors. This works for simple cases like /^bar/ but may produce incorrect suggestions for:

  • Patterns with character classes like /[^abc]/ (negation, not anchor)
  • Patterns with alternations like /^foo|bar/
  • Patterns with quantifiers or special regex syntax

Consider either:

  1. Limiting the rule to simple alphanumeric patterns
  2. Adding a proper regex parser to validate the pattern is a simple literal
📜 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 0196c0e and 1c6c747.

⛔ Files ignored due to path filters (7)
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/valid.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (5)
  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js
  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/valid.js
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
🧠 Learnings (31)
📓 Common learnings
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `RuleSource::Eslint(...).same()` when implementing a rule that matches the behavior of an ESLint rule
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Commit rule work with message format `feat(biome_<language>_analyze): <ruleName>`
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `RuleSource::Eslint(...).inspired()` when implementing a rule inspired by but with different behavior than an ESLint rule
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Add `sources` field with `RuleSource` to cite ESLint or other rules that inspired the implementation
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules should perform static analysis of source code to detect invalid or error-prone patterns and emit diagnostics with proposed fixes
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/lib/**/*.rs : Wrap rule options fields in `Option<>` to properly track set and unset options during merge

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/lib/**/*.rs : Rule options must be placed inside the `biome_rule_options` crate

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/lib/**/*.rs : Use `rename_all = "camelCase"` in serde derive macro for rule options

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/lib/**/*.rs : Use `deny_unknown_fields` in serde derive macro for rule options

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/lib/**/*.rs : Use `Box<[T]>` instead of `Vec<T>` for rule options arrays to save memory

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/lib/**/*.rs : Implement `Merge` trait for rule options to support configuration inheritance

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use language-specific rule names if the rule is meant for a specific language only

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Rule documentation must include `## Options` section if the rule has options

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use generic rule names if the rule could potentially be implemented for multiple languages

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : The first paragraph of rule documentation must be a single line describing what the rule does

Applied to files:

  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `use_options` code block property for code examples that follow an options configuration in documentation

Applied to files:

  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/tests/specs/**/*valid* : Create test files prefixed with `valid` for code that should not trigger the rule

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/tests/specs/**/*invalid* : Create test files prefixed with `invalid` for code that should trigger the rule

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/tests/specs/**/*.jsonc : Use `.jsonc` files to contain arrays of code snippet strings for snapshot tests

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/lib/**/*.rs : Rule options struct must derive `Deserializable`, `Serialize`, `Deserialize`, and optionally `JsonSchema`

Applied to files:

  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `options` code block property for rule-specific configuration snippets in documentation

Applied to files:

  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `full_options` code block property for complete biome.json configuration snippets in documentation

Applied to files:

  • crates/biome_rule_options/src/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Each invalid code example in rule documentation must emit exactly one diagnostic

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js
  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Invalid code examples in rule documentation must be marked with `expect_diagnostic` code block property

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js
📚 Learning: 2025-12-04T13:29:49.287Z
Learnt from: dyc3
Repo: biomejs/biome PR: 8291
File: crates/biome_html_formatter/tests/specs/prettier/vue/html-vue/elastic-header.html:10-10
Timestamp: 2025-12-04T13:29:49.287Z
Learning: Files under `crates/biome_html_formatter/tests/specs/prettier` are test fixtures synced from Prettier and should not receive detailed code quality reviews (e.g., HTTP vs HTTPS, formatting suggestions, etc.). These files are test data meant to validate formatter behavior and should be preserved as-is.

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `RuleSource::Eslint(...).same()` when implementing a rule that matches the behavior of an ESLint rule

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules should perform static analysis of source code to detect invalid or error-prone patterns and emit diagnostics with proposed fixes

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `declare_lint_rule!` macro to declare analyzer rule types and implement the RuleMeta trait

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/nursery/**/*.rs : Add `issue_number` field to `declare_lint_rule!` macro for work-in-progress rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `RuleSource::Eslint(...).inspired()` when implementing a rule inspired by but with different behavior than an ESLint rule

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Add `sources` field with `RuleSource` to cite ESLint or other rules that inspired the implementation

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/nursery/**/*.rs : Place new rules inside the `nursery` group during development

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules should check syntax according to language specification and emit error diagnostics

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Valid code examples in rule documentation should not trigger any diagnostics

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-22T09:26:56.943Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8537
File: crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs:167-210
Timestamp: 2025-12-22T09:26:56.943Z
Learning: When defining lint rules (declare_lint_rule!), only specify fix_kind if the rule implements an action(...) function. Rules that only emit diagnostics without a code fix should omit fix_kind. This applies to all Rust lint rule definitions under crates/.../src/lint (e.g., crates/biome_js_analyze/src/lint/...).

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs (1)
crates/biome_js_syntax/src/expr_ext.rs (1)
  • callee (33-38)
🔇 Additional comments (4)
crates/biome_rule_options/src/use_starts_ends_with.rs (1)

1-6: Well-structured options definition.

The struct correctly follows the biome rule options conventions: deny_unknown_fields, rename_all = "camelCase", and all required trait derivations. As a unit-like struct with no fields, it's ready for future expansion if options are needed. Based on learnings.

crates/biome_rule_options/src/lib.rs (1)

376-376: LGTM!

Module export correctly placed in alphabetical order.

crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js (1)

1-15: Comprehensive invalid test coverage.

Good selection of patterns covering all the detection paths: computed member access, charAt, indexOf, lastIndexOf, slice, substring, match, and regex test. Both startsWith and endsWith variants are exercised.

crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs (1)

94-114: LGTM!

Clear and helpful diagnostic message with appropriate note showing the suggested replacement.

Copy link
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.

A quick review. Make sure to take a look at the bot's comments. Also, needs a changeset.

Comment on lines 102 to 113
Some(
RuleDiagnostic::new(
rule_category!(),
node.range(),
markup! {
"Use `"{method_name}"()` method for string comparison."
},
)
.note(markup! {
"This pattern can be replaced with `"{method_name}"('"{ &state.pattern }"')`."
}),
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Remember the rule pillars (see contributing docs). You've only told the how to fix the error, but not what is the error, and why its an error.

}

impl Rule for UseStartsEndsWith {
type Query = Ast<AnyStringCheckExpression>;
Copy link
Contributor

Choose a reason for hiding this comment

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

The source rule uses type information to only make these suggestions for checks against actual strings. For example foo[0] === "b" could have foo be an array, and this rule would flag it as a false positive.

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 (3)
crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs (3)

408-415: Avoid string allocation in extract_string_literal.

The function returns Option<String> and calls .to_string(), which allocates. According to coding guidelines, avoid string allocations by using TokenText or &str where possible.

♻️ Suggested refactor using TokenText
-fn extract_string_literal(expr: &AnyJsExpression) -> Option<String> {
+fn extract_string_literal(expr: &AnyJsExpression) -> Option<impl std::fmt::Display> {
     if let AnyJsExpression::AnyJsLiteralExpression(literal) = expr
         && let Some(string) = literal.as_js_string_literal_expression()
     {
-        return Some(string.inner_string_text().ok()?.to_string());
+        return Some(string.inner_string_text().ok()?);
     }
     None
 }

You'll need to update UseStartsEndsWithState.pattern to store TokenText instead of String, or evaluate whether the state genuinely needs to own the string data.

Based on learnings, avoid .to_string() allocations where possible.


382-382: Additional string allocations in check_regex.

Lines 382 and 390 use .to_string() to allocate pattern strings. Consider whether TokenText slices could be used here as well to avoid allocations.

Based on learnings, minimise string allocations throughout the rule implementation.

Also applies to: 390-390


280-287: Consider validating pattern length more defensively.

Line 281 uses pattern.chars().count() to calculate pattern length for validation. Whilst correct for Unicode, it could be expensive for long patterns. Additionally, the validation logic assumes the numeric literal precisely equals the pattern length—consider whether off-by-one edge cases or non-literal expressions could bypass this check.

The current logic is sound, but documenting why the length check is necessary (to avoid false positives with lastIndexOf) would aid maintainability.

📜 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 1c6c747 and 7e59b66.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : When porting rules from other linters, use `sources` metadata with `RuleSource::Eslint().same()` for identical behavior or `.inspired()` for different behavior
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Rule names should use the `use` prefix when the rule's sole intention is to mandate a single concept
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Rule names should use the `use` prefix when the rule's sole intention is to mandate a single concept

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Deprecated rules must include a `deprecated` field in the `declare_lint_rule!` macro with an explanation of what rule to use instead

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : New rules must be placed inside the `nursery` group before promotion to other groups

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : When porting rules from other linters, use `sources` metadata with `RuleSource::Eslint().same()` for identical behavior or `.inspired()` for different behavior

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Avoid string allocations by using `&str` or `TokenText` instead of `to_string()`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use `declare_lint_rule!` macro with a `version` field set to `next` for new rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Rule names should use the `no` prefix when the rule's sole intention is to forbid a single concept

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use `rule_category!()` macro to refer to the diagnostic category instead of dynamically parsing its string name

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use the `Semantic<T>` query type to access semantic information about bindings, references, and scope within a rule

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-22T09:26:56.943Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8537
File: crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs:167-210
Timestamp: 2025-12-22T09:26:56.943Z
Learning: When defining lint rules (declare_lint_rule!), only specify fix_kind if the rule implements an action(...) function. Rules that only emit diagnostics without a code fix should omit fix_kind. This applies to all Rust lint rule definitions under crates/.../src/lint (e.g., crates/biome_js_analyze/src/lint/...).

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : The first paragraph of rule documentation must be written in a single line to ensure proper rendering in the rules overview table

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-31T15:35:32.899Z
Learnt from: dyc3
Repo: biomejs/biome PR: 8639
File: crates/biome_js_analyze/src/lint/nursery/no_excessive_lines_per_file.rs:101-108
Timestamp: 2025-12-31T15:35:32.899Z
Learning: In Rust lint rules under the nursery category, the issue_number field in declare_lint_rule! is optional and should not be added unless there is a compelling reason. In code reviews, verify that no unnecessary issue_number is included in nursery lint declarations. Only add issue_number if there is an explicit, justified reason (e.g., tracked issue for external observers). This guidance broadly applies to all nursery lint rule files, not just the single file.

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Mark rules with `issue_number` in the `declare_lint_rule!` macro if they are work-in-progress to indicate missing features

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Code blocks in rule documentation must specify a language identifier and be tagged with `expect_diagnostic` for invalid examples or remain untagged for valid examples

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Invalid code snippets in rule documentation must emit exactly one diagnostic

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Rules should explain to users what the error is, why it is triggered, and what they should do to fix it

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
🔇 Additional comments (4)
crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs (4)

121-438: Overall implementation is solid.

The rule comprehensively covers various string-checking patterns (computed member access, charAt, indexOf, lastIndexOf, slice, substring, match, regex literals). Logic is generally sound, and the dispatch structure is clean.

Nice work porting this rule! The coverage is thorough and the code is well-structured. Once the escape-checking and string-allocation concerns are addressed, this should be ready for merging.


11-16: Reformat the first paragraph to a single line.

The first paragraph of rule documentation must be written in a single line to ensure proper rendering in the rules overview table.

📝 Suggested fix
-    /// Enforce using `String.startsWith()` and `String.endsWith()` over more complex alternatives.
-    ///
-    /// There are multiple ways to verify if a string starts or ends with a certain string,
-    /// such as `foo.indexOf('bar') === 0` or `foo.slice(0, 3) === 'bar'`.
-    ///
-    /// This rule is aimed to enforce a consistent and more readable style using `startsWith()` and `endsWith()` methods.
+    /// Enforce using `String.startsWith()` and `String.endsWith()` over more complex alternatives.
+    ///
+    /// There are multiple ways to verify if a string starts or ends with a certain string, such as `foo.indexOf('bar') === 0` or `foo.slice(0, 3) === 'bar'`. This rule is aimed to enforce a consistent and more readable style using `startsWith()` and `endsWith()` methods.

Based on learnings, the first paragraph must be single-line for proper table rendering.

⛔ Skipped due to learnings
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : The first paragraph of rule documentation must be written in a single line to ensure proper rendering in the rules overview table
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Rule names should use the `use` prefix when the rule's sole intention is to mandate a single concept
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Rule documentation must include an `## Examples` section followed by `### Invalid` and `### Valid` subsections, with Invalid appearing first
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Rule names should use the `no` prefix when the rule's sole intention is to forbid a single concept
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Invalid code snippets in rule documentation must emit exactly one diagnostic
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Code blocks in rule documentation must specify a language identifier and be tagged with `expect_diagnostic` for invalid examples or remain untagged for valid examples
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Deprecated rules must include a `deprecated` field in the `declare_lint_rule!` macro with an explanation of what rule to use instead
Learnt from: dyc3
Repo: biomejs/biome PR: 8639
File: crates/biome_js_analyze/src/lint/nursery/no_excessive_lines_per_file.rs:101-108
Timestamp: 2025-12-31T15:35:41.261Z
Learning: In crates/biome_analyze/**/*analyze/src/lint/nursery/**/*.rs, the `issue_number` field in `declare_lint_rule!` macro is optional and the vast majority of nursery rules do not need it. Do not recommend adding `issue_number` unless there's a specific reason.
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : When porting rules from other linters, use `sources` metadata with `RuleSource::Eslint().same()` for identical behavior or `.inspired()` for different behavior
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use `declare_lint_rule!` macro with a `version` field set to `next` for new rules

427-438: No change required—the text comparison approach is intentional and already documented.

The function uses syntax text comparison by design since the rule operates at the syntactic level (Ast<AnyStringCheckExpression> rather than Semantic<T>). The comment at line 435 already explains this trade-off.

Your concern about false positives from shadowed variables (e.g., different x in nested scopes) is theoretically valid but practically unlikely in this context. The function is only called when verifying specific string-operation patterns, and variable shadowing would be rare at that granularity. If semantic binding verification becomes necessary later for stricter matching, update the comment to explain why.


375-376: The regex escape checking logic is actually correct.

The pattern extraction and checks at lines 375-376 properly distinguish between anchors and escaped characters. For /^bar/, the pattern is ^bar, and for /^\^bar/ (escaped caret after anchor), the pattern is ^\\^bar—the trim removes the anchor and preserves the escaped caret. The second condition (!pattern.starts_with(r"\^")) is redundant but harmless; a string cannot simultaneously start with ^ and \^.

The existing test suite covers the intended use cases (simple anchored patterns). If escaped anchors need additional coverage, consider adding test cases like /^\^bar/, /^\\bar/, etc., but the current implementation handles them correctly.

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: 3

🤖 Fix all issues with AI agents
In @crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs:
- Around line 370-403: check_regex currently returns suggestions for anchored
patterns without ensuring the pattern is a literal string; add validation after
deriving `pattern` (and before returning any `UseStartsEndsWithState`) to reject
patterns containing regex metacharacters. Specifically, detect any unallowed
characters such as . * + ? [ ] { } ( ) | and backslash (treat backslash-escaped
sequences as non-literal) and if any are present return None; perform this check
prior to creating `clean_pattern` and returning from the StartsWith or EndsWith
branches in `check_regex` so only simple literal patterns produce a suggestion.
📜 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 7e59b66 and 6d494fc.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
🧠 Learnings (18)
📓 Common learnings
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : When porting rules from other linters, use `sources` metadata with `RuleSource::Eslint().same()` for identical behavior or `.inspired()` for different behavior
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Rule names should use the `use` prefix when the rule's sole intention is to mandate a single concept

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Deprecated rules must include a `deprecated` field in the `declare_lint_rule!` macro with an explanation of what rule to use instead

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : New rules must be placed inside the `nursery` group before promotion to other groups

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Avoid string allocations by using `&str` or `TokenText` instead of `to_string()`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : When porting rules from other linters, use `sources` metadata with `RuleSource::Eslint().same()` for identical behavior or `.inspired()` for different behavior

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Rule names should use the `no` prefix when the rule's sole intention is to forbid a single concept

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use `declare_lint_rule!` macro with a `version` field set to `next` for new rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Implement custom `Queryable` and `Visitor` types for rules that require deep inspection of child nodes to avoid inefficient traversals

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-22T09:26:56.943Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8537
File: crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs:167-210
Timestamp: 2025-12-22T09:26:56.943Z
Learning: When defining lint rules (declare_lint_rule!), only specify fix_kind if the rule implements an action(...) function. Rules that only emit diagnostics without a code fix should omit fix_kind. This applies to all Rust lint rule definitions under crates/.../src/lint (e.g., crates/biome_js_analyze/src/lint/...).

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use the `Semantic<T>` query type to access semantic information about bindings, references, and scope within a rule

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : The first paragraph of rule documentation must be written in a single line to ensure proper rendering in the rules overview table

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2025-12-31T15:35:32.899Z
Learnt from: dyc3
Repo: biomejs/biome PR: 8639
File: crates/biome_js_analyze/src/lint/nursery/no_excessive_lines_per_file.rs:101-108
Timestamp: 2025-12-31T15:35:32.899Z
Learning: In Rust lint rules under the nursery category, the issue_number field in declare_lint_rule! is optional and should not be added unless there is a compelling reason. In code reviews, verify that no unnecessary issue_number is included in nursery lint declarations. Only add issue_number if there is an explicit, justified reason (e.g., tracked issue for external observers). This guidance broadly applies to all nursery lint rule files, not just the single file.

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Mark rules with `issue_number` in the `declare_lint_rule!` macro if they are work-in-progress to indicate missing features

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Code blocks in rule documentation must specify a language identifier and be tagged with `expect_diagnostic` for invalid examples or remain untagged for valid examples

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Invalid code snippets in rule documentation must emit exactly one diagnostic

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Rules should explain to users what the error is, why it is triggered, and what they should do to fix it

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Check if a variable is global using the semantic model before reporting diagnostics for rules that ban global functions or variables

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs
🔇 Additional comments (5)
crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs (5)

10-62: Rule declaration follows conventions properly.

The metadata is correctly configured with version: "next", proper ESLint source attribution using .same(), and appropriate documentation structure. The first paragraph is a single line as required, and invalid examples are tagged with expect_diagnostic.


64-78: Type definitions are sound.

The String type for the pattern field is appropriate here since the state must be stored and outlive the query context for diagnostic emission.


80-119: LGTM!

The Rule implementation correctly dispatches based on expression type and provides clear, actionable diagnostics.


121-178: LGTM!

The bidirectional checking of binary expression sides ensures the rule catches patterns regardless of operand order.


435-446: Object comparison by syntax text is a reasonable heuristic.

Line 443 compares objects using their text representation. Whilst this could theoretically have edge cases with formatting differences, it's a pragmatic approach for detecting the same object reference in str[str.length - 1] patterns.

Comment on lines +287 to +301
// Validate that the right operand is a numeric literal equal to pattern length
let right = binary.right().ok()?;
let pattern_length = pattern.chars().count();

if let AnyJsExpression::AnyJsLiteralExpression(literal) = right
&& let Some(number) = literal.as_js_number_literal_expression()
&& let Ok(value) = number.value_token()
&& let Ok(parsed) = value.text_trimmed().parse::<usize>()
&& parsed == pattern_length
{
return Some(UseStartsEndsWithState {
variant: StartsEndsWithVariant::EndsWith,
pattern,
});
}
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

Incorrect string length calculation for Unicode strings.

Line 289 uses pattern.chars().count() which counts Unicode scalar values, but JavaScript's lastIndexOf and string length use UTF-16 code units. For strings with characters outside the Basic Multilingual Plane (e.g., emoji, mathematical symbols like 𝟘), this will cause incorrect pattern matching.

For example:

  • JavaScript: "𝟘".length === 2 (UTF-16 code units)
  • Rust: "𝟘".chars().count() === 1 (Unicode scalar values)
🔧 Proposed fix using UTF-16 code unit count
                 let left = binary.left().ok()?;
                 if is_length_access(&left) {
                     // Validate that the right operand is a numeric literal equal to pattern length
                     let right = binary.right().ok()?;
-                    let pattern_length = pattern.chars().count();
+                    let pattern_length = pattern.encode_utf16().count();
                     
                     if let AnyJsExpression::AnyJsLiteralExpression(literal) = right
                         && let Some(number) = literal.as_js_number_literal_expression()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Validate that the right operand is a numeric literal equal to pattern length
let right = binary.right().ok()?;
let pattern_length = pattern.chars().count();
if let AnyJsExpression::AnyJsLiteralExpression(literal) = right
&& let Some(number) = literal.as_js_number_literal_expression()
&& let Ok(value) = number.value_token()
&& let Ok(parsed) = value.text_trimmed().parse::<usize>()
&& parsed == pattern_length
{
return Some(UseStartsEndsWithState {
variant: StartsEndsWithVariant::EndsWith,
pattern,
});
}
// Validate that the right operand is a numeric literal equal to pattern length
let right = binary.right().ok()?;
let pattern_length = pattern.encode_utf16().count();
if let AnyJsExpression::AnyJsLiteralExpression(literal) = right
&& let Some(number) = literal.as_js_number_literal_expression()
&& let Ok(value) = number.value_token()
&& let Ok(parsed) = value.text_trimmed().parse::<usize>()
&& parsed == pattern_length
{
return Some(UseStartsEndsWithState {
variant: StartsEndsWithVariant::EndsWith,
pattern,
});
}

Comment on lines +370 to +403
fn check_regex(regex: &JsRegexLiteralExpression) -> Option<UseStartsEndsWithState> {
let token = regex.value_token().ok()?;
let text = token.text_trimmed();

let first_slash = text.find('/')?;
let last_slash = text.rfind('/')?;

if first_slash >= last_slash {
return None;
}

let pattern = &text[first_slash + 1..last_slash];

let has_start = pattern.starts_with('^') && !pattern.starts_with(r"\^");
let has_end = pattern.ends_with('$') && !pattern.ends_with(r"\$");

if has_start {
let clean_pattern = pattern.trim_start_matches('^');
return Some(UseStartsEndsWithState {
variant: StartsEndsWithVariant::StartsWith,
pattern: clean_pattern.to_string(),
});
}

if has_end {
let clean_pattern = pattern.trim_end_matches('$');
return Some(UseStartsEndsWithState {
variant: StartsEndsWithVariant::EndsWith,
pattern: clean_pattern.to_string(),
});
}

None
}
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

🧩 Analysis chain

🏁 Script executed:

# First, locate the file and understand its full context
find . -name "use_starts_ends_with.rs" -type f

Repository: biomejs/biome

Length of output: 179


🏁 Script executed:

# Look for test cases related to this lint rule
find . -name "*use_starts_ends_with*" -type f

Repository: biomejs/biome

Length of output: 179


🏁 Script executed:

# Search for any validation of metacharacters in the file
rg "metachar|escape|literal" crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs -A 2 -B 2

Repository: biomejs/biome

Length of output: 5054


🌐 Web query:

ESLint prefer-string-starts-ends-with rule regex metacharacters metachar validation

💡 Result:

Short answer

  • The prefer-string-starts-ends-with rules (typescript-eslint and unicorn variants) target cases where code uses string checks or simple anchored regexes like /^foo/ or /foo$/ and can be replaced by foo.startsWith('foo') / foo.endsWith('foo'). [1][2]
  • They do not change regexes that rely on regex metacharacters (character classes, quantifiers, anchors with more complex patterns, lookarounds, etc.), because those have different semantics than a plain literal prefix/suffix; such patterns are not safe to auto-replace. The docs show the rule flags simple anchored literals (e.g. /^bar/, /bar$/) as examples. [1][2]
  • If you worry about invalid/unsupported regex syntax (constructor strings, bad flags), use ESLint’s no-invalid-regexp (or eslint-plugin-regexp) which validates regex literals/constructors per the spec. [3][4]

If you want, I can check a specific regex you’re concerned about and tell whether the rule will flag or auto-fix it.


🏁 Script executed:

# Look for test snapshots or test data
find . -path "*use_starts_ends_with*" -name "*.snap" -o -path "*use_starts_ends_with*" -name "*.js" -o -path "*use_starts_ends_with*" -name "*.json"

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Read the full file to understand the context and any validation
wc -l crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 125


🏁 Script executed:

# Read specific sections around check_regex and look for test cases
head -430 crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs | tail -80

Repository: biomejs/biome

Length of output: 2503


🏁 Script executed:

# Search for test snapshots or test configurations
find . -name "*use_starts_ends_with*" -type f | grep -E "(snap|test)"

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Search more broadly for test files
find . -type f -name "*.rs" | xargs grep -l "use_starts_ends_with" | head -20

Repository: biomejs/biome

Length of output: 95


🏁 Script executed:

# Look at the directory structure for test files
find crates/biome_js_analyze -type d -name "*test*" -o -type d -name "*nursery*"

Repository: biomejs/biome

Length of output: 171


🏁 Script executed:

# Find snapshot files or test data
find crates/biome_js_analyze -name "*.snap" | head -10

Repository: biomejs/biome

Length of output: 772


🏁 Script executed:

# Check if there's a test module in the file itself
grep -n "mod test\|#\[test\]" crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Look at the end of the file to see if there are test cases
tail -100 crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 3302


🏁 Script executed:

# Search for test specifications for this rule
find crates/biome_js_analyze/tests/specs/nursery -name "*useStartsEndsWith*" -o -name "*use_starts_ends_with*"

Repository: biomejs/biome

Length of output: 118


🏁 Script executed:

# List files in the nursery test specs directory
ls crates/biome_js_analyze/tests/specs/nursery/ | grep -i "start\|end"

Repository: biomejs/biome

Length of output: 89


🏁 Script executed:

# Check if there's a directory for this rule
ls -la crates/biome_js_analyze/tests/specs/nursery/ | head -30

Repository: biomejs/biome

Length of output: 1968


🏁 Script executed:

# List files in the useStartsEndsWith test directory
ls -la crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/

Repository: biomejs/biome

Length of output: 426


🏁 Script executed:

# Read the test files to see what patterns are tested
cat crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js

Repository: biomejs/biome

Length of output: 474


🏁 Script executed:

# Also check the valid cases
cat crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/valid.js

Repository: biomejs/biome

Length of output: 138


🏁 Script executed:

# Check if there's a snapshot file with expected outputs
ls crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/*.snap

Repository: biomejs/biome

Length of output: 210


🏁 Script executed:

# Read the snapshot file to see what patterns are tested
head -100 crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js.snap

Repository: biomejs/biome

Length of output: 3196


🏁 Script executed:

# Look at more of the snapshot to see if there are tests with metacharacters
cat crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js.snap | grep -A 5 -B 5 "\.\*\|\\+" | head -50

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Check if there are tests with regex metacharacters in the test files or snapshots
rg "\\.\*|\\[.*\\]|\\\+" crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/

Repository: biomejs/biome

Length of output: 1333


🏁 Script executed:

# Check the entire invalid.js file to be sure
cat crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js

Repository: biomejs/biome

Length of output: 474


🏁 Script executed:

# Now let's trace through the code to understand the full flow
# Read the entire check_regex implementation and see how it's called
sed -n '1,100p' crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 2995


🏁 Script executed:

# Search for where UseStartsEndsWithState is used - to see if there's validation later
rg "UseStartsEndsWithState" crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 1176


🏁 Script executed:

# Let's trace through what happens with a pattern like /^a.*b/
# Read the entire check_regex function and the calling code to see the full flow
sed -n '370,410p' crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 1397


🏁 Script executed:

# Let's verify by creating a test scenario - what pattern would the function return for /^a.*b/?
# First, let's understand how the diagnostic is generated with the pattern
sed -n '50,120p' crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 2111


🏁 Script executed:

# Look for any utility functions that might validate regex patterns
rg "is_literal|validate|metachar" crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Check if there are any comments or documentation about handling regex metacharacters
sed -n '340,410p' crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 2308


🏁 Script executed:

# Look at the full file to see if there's any mention of literal-only patterns
head -50 crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs | tail -40

Repository: biomejs/biome

Length of output: 1127


🏁 Script executed:

# Check if there are integration tests with more complex patterns
find crates -name "*.js" -o -name "*.ts" | xargs grep -l "useStartsEndsWith\|use_starts_ends_with" | head -5

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Let's create a simple test to trace through what would happen with a metacharacter pattern
# First, check if there's any documentation about the rule's scope
cat crates/biome_rule_options/src/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 409


🏁 Script executed:

# Search the entire codebase to see if there are any comments or commits about metacharacters
rg -B 3 -A 3 "check_regex" crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs | head -40

Repository: biomejs/biome

Length of output: 665


🏁 Script executed:

# Let's verify by checking if there's a regex pattern in the test file that might contain metacharacters
cat crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js | grep "/"

Repository: biomejs/biome

Length of output: 177


🏁 Script executed:

# Let me check what the actual behavior would be if we had a pattern with metacharacters
# First, verify the test file doesn't contain any complex patterns
grep -E '\^\^|\.\\*|\[.*\]|\+|\\+' crates/biome_js_analyze/tests/specs/nursery/useStartsEndsWith/invalid.js

Repository: biomejs/biome

Length of output: 443


🏁 Script executed:

# Check if there's any escaping logic or validation in the entire file that I might have missed
wc -l crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs && echo "---" && cat crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs | grep -n "is_literal\|validate\|check.*pattern\|regex.*meta"

Repository: biomejs/biome

Length of output: 318


🏁 Script executed:

# Let's trace through what the code would do step by step with /^a.*b/ pattern
python3 << 'EOF'
# Simulating what check_regex would do with /^a.*b/
regex_text = r"/^a.*b/"

# Find first and last /
first_slash = regex_text.find('/')
last_slash = regex_text.rfind('/')

print(f"Regex text: {regex_text}")
print(f"First slash at: {first_slash}, Last slash at: {last_slash}")

# Extract pattern
pattern = regex_text[first_slash + 1:last_slash]
print(f"Extracted pattern: {pattern}")

# Check for ^ and $
has_start = pattern.startswith('^') and not pattern.startswith('\\^')
print(f"has_start (starts with ^ not \\^): {has_start}")

if has_start:
    # Trim start
    clean_pattern = pattern.lstrip('^')
    print(f"After trimming ^: {clean_pattern}")
    print(f"Would suggest: foo.startsWith('{clean_pattern}')")
    print(f"But regex /^a.*b/ matches 'a' then any chars then 'b'")
    print(f"While startsWith('a.*b') looks for literal string 'a.*b'")
    print(f"These are NOT equivalent!")
EOF

Repository: biomejs/biome

Length of output: 143


🏁 Script executed:

# Let me check if there's any filtering or validation in how the result is used
sed -n '50,100p' crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs

Repository: biomejs/biome

Length of output: 1519


🏁 Script executed:

# Check if there's any documentation or comments about literal-only patterns
grep -B 5 -A 5 "literal\|simple" crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs | head -30

Repository: biomejs/biome

Length of output: 956


Add validation to reject regex patterns with metacharacters.

The check_regex() function extracts patterns from regexes like /^foo/ without checking whether they contain regex metacharacters. For example, /^a.*b/ would suggest startsWith("a.*b"), but the regex matches "a" followed by any characters followed by "b", whilst startsWith() matches the literal string "a.*b". ESLint's rule avoids this by only suggesting replacements for simple anchored regexes.

Validate that the extracted pattern is literal (contains no metacharacters such as ., *, +, ?, [, ], {, }, (, ), |, etc.) before returning a suggestion.

🤖 Prompt for AI Agents
In @crates/biome_js_analyze/src/lint/nursery/use_starts_ends_with.rs around
lines 370 - 403, check_regex currently returns suggestions for anchored patterns
without ensuring the pattern is a literal string; add validation after deriving
`pattern` (and before returning any `UseStartsEndsWithState`) to reject patterns
containing regex metacharacters. Specifically, detect any unallowed characters
such as . * + ? [ ] { } ( ) | and backslash (treat backslash-escaped sequences
as non-literal) and if any are present return None; perform this check prior to
creating `clean_pattern` and returning from the StartsWith or EndsWith branches
in `check_regex` so only simple literal patterns produce a suggestion.

Comment on lines +448 to +469
fn is_string_type(expr: &AnyJsExpression) -> bool {
// If it's a string literal, it's definitely a string
if let AnyJsExpression::AnyJsLiteralExpression(literal) = expr {
if literal.as_js_string_literal_expression().is_some() {
return true;
}
}

// Check for template literals
if matches!(expr, AnyJsExpression::JsTemplateExpression(_)) {
return true;
}

// Check for array literals - these are not strings
if matches!(expr, AnyJsExpression::JsArrayExpression(_)) {
return false;
}

// For now, we allow unknown types or types that could be strings
// This is conservative but helps avoid false positives
true
}
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 | 🟡 Minor

Conservative type checking may cause false positives on arrays.

The function returns true for most expression types (line 468), which means patterns like arr[0] === 'x' on array identifiers will be flagged, even though arrays don't have startsWith/endsWith methods. Whilst the comment notes this is intentional, it could frustrate users with false positives on legitimate array operations.

Consider leveraging semantic model type information if available, or document this limitation in the rule description. For a nursery rule, this tradeoff might be acceptable initially.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Diagnostic Area: diagnostocis A-Linter Area: linter A-Project Area: project L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

📎 Port prefer-string-starts-ends-with from typescript-eslint

2 participants