fix: noDelete should not suggest undefined assignment for process.env#9760
Conversation
The noDelete rule currently suggests replacing delete process.env.FOO with process.env.FOO = undefined. This is incorrect because: 1. delete process.env.FOO is the official documented way to remove env vars 2. Assigning undefined has different behavior from delete for process.env 3. The suggestion can break functionality (e.g., AWS Lambda env reset patterns) The rule still reports the diagnostic for process.env deletions, but no longer offers the unsafe code action. Fixes biomejs#4093
🦋 Changeset detectedLatest commit: 6c0e8ad The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
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 |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThe 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
crates/biome_js_analyze/src/lint/performance/no_delete.rs (1)
135-145: This branch appears to be dead code for the intended use caseLines 135-145 check if
static_obj.object()is a static member expression with member"process". This would match patterns likedelete a.process.env.FOO— whereprocessis itself a property of another object.For the common case
delete process.env.FOO,static_obj.object()is the identifierprocess, so this branch is never entered. You may want to either:
- Remove this branch if it's not needed
- Add a test case for nested patterns like
delete globalThis.process.env.FOOif that's the intended coverage🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_js_analyze/src/lint/performance/no_delete.rs` around lines 135 - 145, The branch that checks static_obj.object().ok().and_then(|o| o.as_js_static_member_expression()) and then inspects outer_object.member()/outer_member.as_js_name() for "process" (which would match nested patterns like delete a.process.env.FOO) appears to be dead for the common case delete process.env.FOO where static_obj.object() is an identifier; remove this entire nested block (the if let Some(outer_object) { ... } section that returns None) to eliminate unreachable code, or if you intended to support nested targets, instead keep the block and add a unit test exercising delete globalThis.process.env.FOO (or similar nested pattern) to validate the behavior; update tests accordingly and ensure the code paths around static_obj.object(), as_js_static_member_expression(), outer_object.member(), and outer_member.as_js_name() reflect the chosen approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/biome_js_analyze/src/lint/performance/no_delete.rs`:
- Around line 146-156: The current check incorrectly calls
object.as_js_identifier_expression() for member expressions like delete
process.env.FOO, causing the guard to miss when the base is a static member;
change the check to use static_obj.object().as_js_identifier_expression() (i.e.,
call .object() on the static member expression `static_obj` instead of using
`object`) so the subsequent name extraction (ident.name(),
name_ref.as_js_reference_identifier(), and the comparison
name.to_trimmed_text().text() == "process") correctly detects `process.env`
cases and prevents suggesting the fix for delete process.env.FOO.
- Around line 130-161: The current check using
argument.as_js_static_member_expression() cannot detect destructured bindings
like `const { env } = process; delete env.FOO;`; either remove those
destructured test cases and restrict the rule to only match direct static
members (i.e., only rely on argument.as_js_static_member_expression() /
static_obj.member() / name.to_trimmed_text()) or implement semantic binding
resolution: use the rule's context model (ctx.model()) to resolve identifier
origins when object.as_js_identifier_expression() yields an identifier (follow
the identifier's binding to see if it ultimately points to process.env) and only
then treat it as exempt; update the matching logic around
argument/as_js_static_member_expression(), object.as_js_identifier_expression(),
and the name checks to use the model-based binding lookup if you choose the
semantic route.
In `@crates/biome_js_analyze/tests/specs/performance/noDelete/invalid.jsonc`:
- Around line 14-16: The test cases reveal two issues: the rule that flags
"delete process.env.FOO" is checking the wrong identifier so it wrongly offers a
fixer; update the checker in the rule implementation that inspects
MemberExpression nodes (the logic around process.env detection) to ensure it
explicitly verifies the object is the Identifier "process" and the property
chain includes "env" before suggesting a fix; for the destructured patterns
("const { env } = process; delete env.FOO;" and "const { env } =
require('process'); delete env.TEST;") do not attempt a syntactic fixer—either
add semantic resolution to trace that env originates from process or
conservatively disable fix suggestions for Identifier-based deletes (i.e., only
auto-fix when the MemberExpression target is directly process.env), and after
fixing, regenerate the spec snapshots with the provided cargo test command to
confirm diagnostics no longer include fix suggestions.
---
Nitpick comments:
In `@crates/biome_js_analyze/src/lint/performance/no_delete.rs`:
- Around line 135-145: The branch that checks
static_obj.object().ok().and_then(|o| o.as_js_static_member_expression()) and
then inspects outer_object.member()/outer_member.as_js_name() for "process"
(which would match nested patterns like delete a.process.env.FOO) appears to be
dead for the common case delete process.env.FOO where static_obj.object() is an
identifier; remove this entire nested block (the if let Some(outer_object) { ...
} section that returns None) to eliminate unreachable code, or if you intended
to support nested targets, instead keep the block and add a unit test exercising
delete globalThis.process.env.FOO (or similar nested pattern) to validate the
behavior; update tests accordingly and ensure the code paths around
static_obj.object(), as_js_static_member_expression(), outer_object.member(),
and outer_member.as_js_name() reflect the chosen approach.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d7bcd120-db5a-4606-bd2f-575345d007fc
📒 Files selected for processing (2)
crates/biome_js_analyze/src/lint/performance/no_delete.rscrates/biome_js_analyze/tests/specs/performance/noDelete/invalid.jsonc
| "delete process.env.FOO;", | ||
| "const { env } = process; delete env.FOO;", | ||
| "const { env } = require('process'); delete env.TEST;" |
There was a problem hiding this comment.
Test cases may not behave as intended given implementation bugs
Good additions for coverage. However:
-
Line 14 (
delete process.env.FOO) — the rule has a bug (wrong variable checked), so this will incorrectly receive a fix suggestion until the implementation is corrected. -
Lines 15-16 (destructured patterns) — these cannot be handled by the current syntactic implementation. The rule would need semantic analysis to track that
envoriginated fromprocess.
Once the implementation issues are resolved, please regenerate snapshots with cargo test -p biome_js_analyze --test spec_tests -- --update to confirm the expected behaviour (diagnostics without fix suggestions). As per coding guidelines, all code changes must include appropriate tests that validate the fix.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/biome_js_analyze/tests/specs/performance/noDelete/invalid.jsonc`
around lines 14 - 16, The test cases reveal two issues: the rule that flags
"delete process.env.FOO" is checking the wrong identifier so it wrongly offers a
fixer; update the checker in the rule implementation that inspects
MemberExpression nodes (the logic around process.env detection) to ensure it
explicitly verifies the object is the Identifier "process" and the property
chain includes "env" before suggesting a fix; for the destructured patterns
("const { env } = process; delete env.FOO;" and "const { env } =
require('process'); delete env.TEST;") do not attempt a syntactic fixer—either
add semantic resolution to trace that env originates from process or
conservatively disable fix suggestions for Identifier-based deletes (i.e., only
auto-fix when the MemberExpression target is directly process.env), and after
fixing, regenerate the spec snapshots with the provided cargo test command to
confirm diagnostics no longer include fix suggestions.
|
@myx0m0p you must run the tests so you can commit the snapshots. Without it, we can't merge the PR. Can't you find a solution? If not, let us know. We can help but it will take some time. As for the rule, we shouldn't trigger the rule at all. The whole point was to comply with Node.js suggestion, where they document to suggest the use of Instead, the rule must not trigger |
|
Thanks for the feedback! I've updated the PR:
|
| "@biomejs/biome": "patch" | ||
| --- | ||
|
|
||
| fix: noDelete should not suggest undefined assignment for process.env |
There was a problem hiding this comment.
We guidelines for how to write a changeset, can you follow them?
Merging this PR will not alter performance
Comparing Footnotes
|
|
@myx0m0p CI failed, please address the failures. Make sure to run the jobs locally so that you can catch things early |
|
all green, @ematipico thanks for onboarding! |
|
Thank you @myx0m0p, sorry to ask you something again, but would you mind also updating the docs of the rule? That's something really important worth mentioning |
|
Pushed follow-up commit
Local verification:
|
Summary
The
noDeleterule currently suggests replacingdelete process.env.FOOwithprocess.env.FOO = undefined. This is incorrect because:delete process.env.FOOis the official documented way to remove env varsundefinedhas different behavior fromdeleteforprocess.envpropertiesChanges
crates/biome_js_analyze/src/lint/performance/no_delete.rs:fn action()to returnNonewhen the deleted target is aprocess.envpropertyprocess.env.FOOand destructured patterns likeconst { env } = process; delete env.FOOtests/specs/performance/noDelete/invalid.jsonc:delete process.env.FOOand destructured env variantsTesting
The diagnostic is still reported for
delete process.env.FOO(it is still a use of thedeleteoperator), but the unsafe code action (assigningundefined) is no longer offered.cargo testlocally as Rust toolchain is not available in the current environment. CI will validate.Fixes #4093