biome style/useTrimStartEnd
labelled safe
Analysis Nemotron 3 Super
Flags calls to String.trimLeft() and trimRight() and suggests replacing them with the semantically identical but preferred String.trimStart() and trimEnd().
Fix: Replaces .trimLeft() with .trimStart() and .trimRight() with .trimEnd(), preserving all surrounding syntax including method call parentheses and any chained expressions.
11 hazards
- The rewrite assumes the base expression is a string; if it is not, both the original and rewritten code will throw a TypeError at runtime, so behavior is preserved but errors may occur where none were expected.
- If the method is accessed via a property name that is dynamically computed (e.g., obj[method]()), changing the hardcoded name from 'trimLeft' to 'trimStart' alters which property is looked up, potentially causing a different function to be called or undefined to be invoked.
- If the code relies on the method being non-enumerable or having specific descriptors (unlikely for built-ins), the rewrite does not affect descriptors since it only changes the identifier used in property access.
- In rare cases where trimLeft or trimRight has been overridden on String.prototype or an instance, the rewrite bypasses the override by calling the standard trimStart/trimEnd methods, altering behavior.
- If the call is part of a larger expression with side effects (e.g., in a logical chain like foo && bar.trimLeft()), the rewrite does not change evaluation order or side-effect timing.
- Comments attached directly to the method name (e.g., trimLeft/*comment*/()) would be lost because the autofix replaces the entire identifier, stripping any inline comments.
- If the code uses a with statement that binds a variable named trimLeft or trimStart, the lookup semantics could differ, though with is deprecated and rarely used.
- In strict mode, if trimLeft or trimStart is mistakenly referenced as a variable due to a typo, the rewrite could turn a ReferenceError into a valid method call (or vice versa), but this is outside the rule's scope as it only applies to property accesses.
- If the base expression evaluates to an object with a custom trimLeft property that is not a function (e.g., a getter with side effects), replacing the property name changes which getter is invoked.
- The autofix does not preserve trailing whitespace or formatting inside the method call parentheses (which are empty anyway), so no behavioral impact there.
- If the method is called via optional chaining (e.g., foo?.trimLeft()), the rewrite correctly preserves the optional chaining operator, so no hazard arises from that form.
Findings
| program | oracle | label | judge | status | summary |
|---|---|---|---|---|---|
| r1-p004 | fix-changes-behavior | labelled safe | intended-change · low | no match | The rule intentionally replaces the deprecated aliases trimLeft/trimRight with the standard trimStart/trimEnd methods. The program calls trimLeft on a plain ob… |
| r1-p005 | fix-changes-behavior | labelled safe | introspection-only · low | no match | The program throws a TypeError because `s` is null, and the error message includes the method name that was called. The fix changes `trimLeft` to its alias `tr… |
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Control Flow Interference | label/break/continue control flow, finally blocks, early returns in nested blocks |
| r1-p002 | invalid | With Statement Lookup Hazard | If the code uses a with statement that binds a variable named trimLeft or trimStart, the lookup semantics could differ, though with is deprecated and rarely used. |
| r1-p003 | fix ok | Trailing Whitespace Preservation | The autofix does not preserve trailing whitespace or formatting inside the method call parentheses (which are empty anyway), so no behavioral impact there. |
| r1-p004 | fix-changes-behavior | Coercion Interactions | numeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt |
| r1-p005 | fix-changes-behavior | Exception Safety | exceptions thrown inside the code the fix removes or rewrites |
| r1-p006 | silent prediction off | Prototype and Property Access | __proto__ keys, prototype access, Object.assign vs spread |
| r1-p007 | fix ok | Property Descriptor Sensitivity | If the code relies on the method being non-enumerable or having specific descriptors (unlikely for built-ins), the rewrite does not affect descriptors since it only changes the identifier used in prop |
| r1-p008 | fix ok | Optional Chaining Preservation | If the method is called via optional chaining (e.g., foo?.trimLeft()), the rewrite correctly preserves the optional chaining operator, so no hazard arises from that form. |
| r2-p002 | fix ok | Side effects in logical chain | Evaluation order and side-effect timing must be preserved in logical expressions. |
| r2-p003 | fix ok | Async/await ordering | Rewriting must not alter await timing or promise resolution order. |
| r2-p004 | fix ok | Inline comment on method name | Autofix strips inline comments attached to the method identifier. |
| r2-p005 | fix ok | Comment on removed code line | Comments on the line containing the trimmed method may be lost after rewrite. |
| r2-p006 | silent prediction off | Function declaration in block | Block-scoped function declarations must not be disturbed by rewrites in their scope. |
| r2-p007 | fix ok | Trailing whitespace in parentheses | Autofix does not change whitespace inside method call parentheses (irrelevant here). |
| r2-p008 | fix ok | Order of evaluation with side effects | Side effects in surrounding expressions must not run twice or be skipped. |
Sources
r1-p001 · Control Flow Interference
1outer: {
2 let s = ' hello ';
3 inner: {
4 if (true) break outer;
5 console.log(s.trimLeft());
6 }
7}
8console.log('done');
--- orig.js 2026-10-17 17:07:29.974662689 -0400 +++ fixed.js 2026-10-17 17:07:30.251665670 -0400 @@ -2,7 +2,7 @@ let s = ' hello '; inner: { if (true) break outer; - console.log(s.trimLeft()); + console.log(s.trimStart()); } } console.log('done');
done
r1-p002 · With Statement Lookup Hazard (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-07-1)
1(function() {
2 var trimLeft = function() { return 'BAD'; };
3 with ({ s: ' x ' }) {
4 console.log(s.trimLeft());
5 }
6})();
r1-p003 · Trailing Whitespace Preservation
1let s = ' test ';
2console.log(s.trimLeft().length);
--- orig.js 2026-10-17 17:07:30.464667962 -0400 +++ fixed.js 2026-10-17 17:07:30.747671006 -0400 @@ -1,2 +1,2 @@ let s = ' test '; -console.log(s.trimLeft().length); +console.log(s.trimStart().length);
6
r1-p004 · Coercion Interactions
1let obj = {
2 toString: () => ' abc '
3};
4console.log((obj).trimLeft() + 'def');
--- orig.js 2026-10-17 17:07:30.919672857 -0400 +++ fixed.js 2026-10-17 17:07:31.183675697 -0400 @@ -1,4 +1,4 @@ let obj = { toString: () => ' abc ' }; -console.log((obj).trimLeft() + 'def'); +console.log((obj).trimStart() + 'def');
r1-p005 · Exception Safety
1let s = null;
2try {
3 console.log(s.trimLeft());
4} catch (e) {
5 console.log(e.name + ': ' + e.message);
6}
--- orig.js 2026-10-17 17:07:31.355677548 -0400 +++ fixed.js 2026-10-17 17:07:31.629680495 -0400 @@ -1,6 +1,6 @@ let s = null; try { - console.log(s.trimLeft()); + console.log(s.trimStart()); } catch (e) { console.log(e.name + ': ' + e.message); }
TypeError: Cannot read properties of null (reading 'trimLeft')
r1-p006 · Prototype and Property Access
1let s = ' hi ';
2let o = { __proto__: String.prototype };
3console.log(o.trimLeft.call(s));
hi
r1-p007 · Property Descriptor Sensitivity
1let s = ' x ';
2let desc = Object.getOwnPropertyDescriptor(String.prototype, 'trimLeft');
3console.log(desc.enumerable);
4console.log(s.trimLeft());
--- orig.js 2026-10-17 17:07:32.022684723 -0400 +++ fixed.js 2026-10-17 17:07:32.287687573 -0400 @@ -1,4 +1,4 @@ let s = ' x '; let desc = Object.getOwnPropertyDescriptor(String.prototype, 'trimLeft'); console.log(desc.enumerable); -console.log(s.trimLeft()); +console.log(s.trimStart());
false x
r1-p008 · Optional Chaining Preservation
1let s = null;
2console.log(s?.trimLeft() ?? 'null');
3s = ' ok ';
4console.log(s?.trimLeft());
--- orig.js 2026-10-17 17:07:32.450689326 -0400 +++ fixed.js 2026-10-17 17:07:32.720692230 -0400 @@ -1,4 +1,4 @@ let s = null; -console.log(s?.trimLeft() ?? 'null'); +console.log(s?.trimStart() ?? 'null'); s = ' ok '; -console.log(s?.trimLeft()); +console.log(s?.trimStart());
null ok
r2-p002 · Side effects in logical chain
1let side = 0;
2function log() { return ++side; }
3const result = log() && ' hello'.trimLeft();
4console.log(`side=${side}, result='${result}'`);
--- orig.js 2026-10-17 17:07:37.141739749 -0400 +++ fixed.js 2026-10-17 17:07:37.431742865 -0400 @@ -1,4 +1,4 @@ let side = 0; function log() { return ++side; } -const result = log() && ' hello'.trimLeft(); +const result = log() && ' hello'.trimStart(); console.log(`side=${side}, result='${result}'`);
side=1, result='hello'
r2-p003 · Async/await ordering
1async function test() {
2 const p = Promise.resolve(' world');
3 const a = await p;
4 const b = a.trimRight();
5 console.log(`'${b}'`);
6}
7test().catch(e => console.error(e));
--- orig.js 2026-10-17 17:07:37.593744604 -0400 +++ fixed.js 2026-10-17 17:07:37.859747462 -0400 @@ -1,7 +1,7 @@ async function test() { const p = Promise.resolve(' world'); const a = await p; - const b = a.trimRight(); + const b = a.trimEnd(); console.log(`'${b}'`); } test().catch(e => console.error(e));
' world'
r2-p004 · Inline comment on method name
1const s = ' hello';
2console.log(s.trimLeft/* keep this */());
--- orig.js 2026-10-17 17:07:38.048749492 -0400 +++ fixed.js 2026-10-17 17:07:38.329752510 -0400 @@ -1,2 +1,2 @@ const s = ' hello'; -console.log(s.trimLeft/* keep this */()); +console.log(s.trimStart/* keep this */());
hello
r2-p005 · Comment on removed code line
1// trim whitespace from input
2const input = ' test ';
3const out = input.trimLeft(); // keep this comment
4console.log(`'${out}'`);
--- orig.js 2026-10-17 17:07:38.503754378 -0400 +++ fixed.js 2026-10-17 17:07:38.775757300 -0400 @@ -1,4 +1,4 @@ // trim whitespace from input const input = ' test '; -const out = input.trimLeft(); // keep this comment +const out = input.trimStart(); // keep this comment console.log(`'${out}'`);
'test '
r2-p006 · Function declaration in block
1{
2 function f() { return ' ok '; }
3 const result = f().trimEnd();
4 console.log(`'${result}'`);
5}
' ok'
r2-p007 · Trailing whitespace in parentheses
1const s = ' foo';
2console.log(s.trimLeft( /* no args */ ));
--- orig.js 2026-10-17 17:07:39.156761391 -0400 +++ fixed.js 2026-10-17 17:07:39.414764161 -0400 @@ -1,2 +1,2 @@ const s = ' foo'; -console.log(s.trimLeft( /* no args */ )); +console.log(s.trimStart( /* no args */ ));
foo
r2-p008 · Order of evaluation with side effects
1let count = 0;
2function inc() { return ++count; }
3const vals = [inc(), inc()].map(v => `${v}`.trimLeft());
4console.log(`count=${count}, vals=${vals}`);
--- orig.js 2026-10-17 17:07:39.578765922 -0400 +++ fixed.js 2026-10-17 17:07:39.851768853 -0400 @@ -1,4 +1,4 @@ let count = 0; function inc() { return ++count; } -const vals = [inc(), inc()].map(v => `${v}`.trimLeft()); +const vals = [inc(), inc()].map(v => `${v}`.trimStart()); console.log(`count=${count}, vals=${vals}`);
count=2, vals=1,2