fixhunt

biome complexity/noUselessEscapeInRegex

labelled safe
Runs / 2026-10-17T20-06-16-162Z-ziws-biome-complexity_noUselessEscapeInRegexbiome 2.5.152026-10-17 20:06 UTC8 programs × 2 round(s)16 programs, 2 fired, 2 fixed, 0 findings, 0 rejectedmodel cost $0.0032

Analysis Nemotron 3 Super

The rule flags unnecessary backslash escapes inside regular expression literals where escaping has no semantic effect.

Fix: Remove the redundant backslash before a character that does not require escaping in a regex literal, preserving all other characters and flags unchanged.

10 hazards
  • Removing '\' before ']' in a character class like '/[\]]/' changes it to '/[]/', which is an empty character class and causes a SyntaxError.
  • Removing '\' before '^' at start of a character class like '/[\^abc]/' changes it to '/[^abc]/', inverting the meaning of the class.
  • Removing '\' before '-' inside a character class like '/[a\-z]/' changes it to '/[a-z]/', altering the range from literal 'a', '-', 'z' to the range 'a' through 'z'.
  • Removing '\' before '[' in a pattern like '/[\[\]]/' changes it to '/[[]]/', which matches a literal '[' instead of '[' or ']'.
  • Removing '\' before a flagged delimiter like in '/\//' (if misparsed) could break the regex if confused with comment syntax, though rare in JS.
  • Removing '\' before whitespace like '\s' in '/[\\s]/' changes it to '/[\s]/', which now matches any whitespace instead of literal backslash and 's'.
  • Removing '\' before a Unicode escape like '/\u{1F600}/' breaks it into '/u{1F600}/', which is invalid syntax and throws.
  • Removing '\' before a digit like '/\1/' in a regex changes it to '/1/', losing the backreference and matching literal '1' instead.
  • Removing '\' before a literal '}' in a quantifier like '/a\{3}/' changes it to '/a{3}/', which is valid but now means 'aaa' instead of literal 'a{3}'.
  • Removing '\' before a literal '(' like '/\(foo\)/' changes it to '/(foo)/', turning a literal match into a capturing group, altering group count and behavior.

Programs

idresulttitlehazard
r1-p001silent prediction offHazard 1: Character class dash escape removal alters rangeRemoving '\' before '-' inside a character class like '/[a\-z]/' changes it to '/[a-z]/', altering the range from literal 'a', '-', 'z' to the range 'a' through 'z'.
r1-p002fix okHazard 2: Arguments object interaction with regexarguments object, function.length, default parameters, rest parameters
r1-p003silent prediction offHazard 3: Null/undefined distinction with optional chainingnull/undefined distinction, optional chaining short-circuit, nullish vs falsy
r1-p004silentHazard 4: Flagged delimiter confusion with comment syntaxRemoving '\' before a flagged delimiter like in '/\//' (if misparsed) could break the regex if confused with comment syntax, though rare in JS.
r1-p005silent prediction offHazard 5: Getter/setter side effects with regexgetters, setters and Object.defineProperty side effects
r1-p006silent prediction offHazard 6: Whitespace escape removal in char class changes meaningRemoving '\' before whitespace like '\s' in '/[\\s]/' changes it to '/[\s]/', which now matches any whitespace instead of literal backslash and 's'.
r1-p007silent prediction offHazard 7: Async/await ordering with regex side effectasync/await ordering, generators and yield position
r1-p008silent prediction offHazard 8: Parenthesis escape removal creates capturing groupRemoving '\' before a literal 'like '/\(foo\)/' changes it to '/(foo)/', turning a literal match into a capturing group, altering group count and behavior.
r2-p001silent prediction off^ negation inversionRemoving '\' before '^' at start of a character class like '/[\^abc]/' changes it to '/[^abc]/', inverting the meaning of the class.
r2-p002silent prediction offBackreference lossRemoving '\' before a digit like '/\1/' in a regex changes it to '/1/', losing the backreference and matching literal '1' instead.
r2-p003fix okBracket literal lossRemoving '\' before '[' in a pattern like '/[\[\]]/' changes it to '/[[]]/', which matches a literal '[' instead of '[' or ']'.
r2-p004silent prediction offWhitespace class meaning shiftRemoving '\' before whitespace like '\s' in '/[\\s]/' changes it to '/[\s]/', which now matches any whitespace instead of literal backslash and 's'.
r2-p005silent__proto__ vs spread__proto__ keys, prototype access, Object.assign vs spread
r2-p006silentBlock function scopingfunction declarations inside blocks, switch cases, labeled statements
r2-p007silent prediction offParenthesis to capturing groupRemoving '\' before a literal '(' like '/\(foo\)/' changes it to '/(foo)/', turning a literal match into a capturing group, altering group count and behavior.
r2-p008silent prediction offArguments object interactionarguments object, function.length, default parameters, rest parameters

Sources

r1-p001 · Hazard 1: Character class dash escape removal alters range
1function test() {
2 const re = /[a\-z]/;
3 let result = '';
4 for (const c of 'az-') {
5 if (re.test(c)) result += c;
6 }
7 console.log(result); // Should be 'a-z' if literal hyphen preserved; becomes 'az' if range
8}
9test();
az-
r1-p002 · Hazard 2: Arguments object interaction with regex
1function f(a, b = /[\-]/, ...rest) {
2 console.log(arguments.length, b.source);
3}
4f(1, 2, 3); // Before fix: b=/[\-]/ → source='[\-]' → matches literal '-'
5 // After fix : b=/[-]/ → source='[-]' → matches range (invalid? but treated as literal '-' in some engines?)
6 // Actually: in JS, [-] in char class is invalid range → throws SyntaxError
7 // So we catch it to show behavioral difference via exception
--- orig.js	2026-10-17 16:06:26.313747208 -0400
+++ fixed.js	2026-10-17 16:06:26.579734060 -0400
@@ -1,4 +1,4 @@
-function f(a, b = /[\-]/, ...rest) {
+function f(a, b = /[-]/, ...rest) {
   console.log(arguments.length, b.source);
 }
 f(1, 2, 3); // Before fix: b=/[\-]/ → source='[\-]' → matches literal '-'
3 undefined
r1-p003 · Hazard 3: Null/undefined distinction with optional chaining
1const obj = { get re() { return /[\\]/; } };
2try {
3 const m = obj.re?.test('\\');
4 console.log(m); // Should be true if \\ matches literal backslash
5} catch (e) {
6 console.log(e.name, e.message);
7}
true
r1-p004 · Hazard 4: Flagged delimiter confusion with comment syntax
1// This is a near-miss: Biome does not trigger on /\// because / is not flagged as needing escape in regex context.
2// We show it does not fire by using it safely.
3const re = /\//;
4console.log(re.test('/')); // Always true; no change from fix since rule doesn't trigger
true
r1-p005 · Hazard 5: Getter/setter side effects with regex
1let cache = null;
2Object.defineProperty(Object.prototype, 'regex', {
3 get() {
4 if (!cache) cache = /[\\s]/; // Intended: literal backslash or 's'
5 return cache;
6 }
7});
8try {
9 console.log('x'.match(/[\\s]/)); // Should match nothing → null
10 console.log('x'.match(/[\s]/)); // After fix: matches whitespace → null (still)
11 // But if we had a string with space...
12 console.log(' x'.match(/[\s]/)); // After fix: matches space → [' ']
13 // Before fix: /[\\s]/ → no match → null
14} catch (e) {
15 console.log(e.name, e.message);
16}
null
null
[ ' ', index: 0, input: ' x', groups: undefined ]
r1-p006 · Hazard 6: Whitespace escape removal in char class changes meaning
1const re = /[\\s]/;
2console.log(re.test('\\')); // true before fix: matches literal backslash
3console.log(re.test('s')); // false before fix
4console.log(re.test(' ')); // false before fix: space not matched
5// After fix: /[\s]/ → matches any whitespace
6// So we test space to see if it starts matching
7console.log('After fix simulation:', /[\s]/.test(' ')); // true → behavioral change
true
true
false
After fix simulation: true
r1-p007 · Hazard 7: Async/await ordering with regex side effect
1async function process() {
2 const re = /[\\d]/; // Intended: literal backslash or 'd'
3 const val = await Promise.resolve('\\');
4 if (re.test(val)) {
5 return 'matched backslash';
6 }
7 return 'no match';
8}
9process().then(console.log);
10// Before fix: matches '\\' → 'matched backslash'
11// After fix : /[\d]/ matches digits → '\\' is not digit → 'no match'
matched backslash
r1-p008 · Hazard 8: Parenthesis escape removal creates capturing group
1const re = /\(foo\)/;
2const str = '(foo)';
3const match = str.match(re);
4console.log(match ? match.length : 0); // Before fix: 1 (only full match)
5 // After fix: /(foo)/ → 2 (full match + capture group 1)
1
r2-p001 · ^ negation inversion
1const re = /[\^abc]/;
2let result = '';
3for (const c of ['^', 'a', 'b', 'c']) {
4 result += re.test(c) ? '1' : '0';
5}
6console.log(result);
1111
r2-p002 · Backreference loss
1function test() {
2 const re = /(.)\1/;
3 return re.test('aa') && !re.test('ab');
4}
5console.log(test());
true
r2-p003 · Bracket literal loss
1const re = /[\[\]]/;
2console.log(re.test('[') + ',' + re.test(']') + ',' + re.test('a'));
--- orig.js	2026-10-17 16:06:32.265454724 -0400
+++ fixed.js	2026-10-17 16:06:32.541441253 -0400
@@ -1,2 +1,2 @@
-const re = /[\[\]]/;
+const re = /[[\]]/;
 console.log(re.test('[') + ',' + re.test(']') + ',' + re.test('a'));
true,true,false
r2-p004 · Whitespace class meaning shift
1const re = /[\\s]/;
2console.log(re.test('\\') + ',' + re.test('s') + ',' + re.test(' '));
true,true,false
r2-p005 · __proto__ vs spread
1const obj = { __proto__: { x: 1 }, y: 2 };
2const copy1 = { ...obj };
3const copy2 = Object.assign({}, obj);
4console.log(copy1.hasOwnProperty('__proto__') + ',' + copy2.hasOwnProperty('__proto__'));
false,false
r2-p006 · Block function scoping
1{
2 function f() { return 1; }
3 {
4 function f() { return 2; }
5 console.log(f());
6 }
7 console.log(f());
8}
2
1
r2-p007 · Parenthesis to capturing group
1const re = /\(foo\)/;
2const str = 'foo';
3const match = re.exec(str);
4console.log(match ? match.length : 0);
0
r2-p008 · Arguments object interaction
1function f(a, b = 1, ...rest) {
2 const re = /\d/;
3 return [arguments.length, f.length, rest.length, re.test('a')];
4}
5console.log(f().join(','));
0,1,0,false