fixhunt

biome complexity/noAdjacentSpacesInRegex

labelled safe
Runs / 2026-10-17T20-03-48-850Z-01ch-biome-complexity_noAdjacentSpacesInRegexbiome 2.5.152026-10-17 20:03 UTC8 programs × 2 round(s)16 programs, 15 fired, 15 fixed, 2 findings, 1 rejectedmodel cost $0.05

Analysis Nemotron 3 Super

The rule flags regex literals containing two or more adjacent literal space characters.

Fix: Replaces every maximal sequence of two or more consecutive literal space characters inside a regex literal with exactly one space character.

10 hazards
  • Changes meaning when spaces are significant, e.g., in patterns like '/a b/' where two spaces are required to match.
  • Breaks quantifier semantics if spaces are part of a quantified subpattern, e.g., '/(x ){2}/' becomes '/(x ){2}/', altering repetition count.
  • Alters behavior in free-spacing mode (/x flag) where literal spaces are ignored and the fix removes meaningful visual spacing.
  • Affects regexes relying on exact space counts for parsing, such as fixed-format text matchers like '/\d{3} \d{2}/'.
  • Modifies patterns using spaces as delimiters in alternatives, e.g., '/foo | bar/' becomes '/foo | bar/', changing token boundaries.
  • Impacts negated character classes containing spaces, e.g., '/[^ ] /' becomes '/[^ ] /', altering what characters are matched.
  • Disrupts tab-separated value parsers where literal spaces are significant, e.g., '/\t \t/' becomes '/\t \t/'.
  • Removes intentional formatting in complex regexes where spaces improve readability without affecting semantics, but the fix still alters the literal.
  • May break code that generates regexes via string concatenation relying on exact space counts from literals, e.g., `new RegExp('/' + pattern + '/')`.
  • Interferes with source inspection or debugging where the original literal's space count is logged or compared.

Findings

programoraclelabeljudgestatussummary
r1-p008fix-changes-behaviorlabelled safeintrospection-only · lowduplicateThe fix rewrites the regex literal from /a b c/ to /a {2}b {2}c/, which changes the value of RegExp.prototype.source. The program only inspects this source t…
r2-p001fix-changes-behaviorlabelled safeintrospection-only · lowrelatedThe fix rewrites the regex literal from `/hello world/` to `/hello {2}world/`, which changes the value of `RegExp.prototype.source` that the program explicitl…

Programs

idresulttitlehazard
r1-p001fix okNull/Undefined Distinctionnull/undefined distinction, optional chaining short-circuit, nullish vs falsy
r1-p002fix okOrder of Evaluation Merged/Duplicatedorder of evaluation when expressions are merged, duplicated or deleted (side effects run twice or never)
r1-p003fix okComments Attached to Removed Codecomments attached to the removed or moved code
r1-p004fix okStrict vs Sloppy Mode Semanticsstrict vs sloppy mode semantics
r1-p005invalidFree-Spacing Mode (/x Flag)Alters behavior in free-spacing mode (/x flag) where literal spaces are ignored and the fix removes meaningful visual spacing.
r1-p006fix okIntentional Formatting for ReadabilityRemoves intentional formatting in complex regexes where spaces improve readability without affecting semantics, but the fix still alters the literal.
r1-p007fix okArguments Object and Function Lengtharguments object, function.length, default parameters, rest parameters
r1-p008fix-changes-behaviorSource Inspection or Debugging InterferenceInterferes with source inspection or debugging where the original literal's space count is logged or compared.
r2-p001fix-changes-behaviorGetter/setter accessing modified regex sourcegetters, setters and Object.defineProperty side effects
r2-p002fix okVar hoisting captures altered regex before redeclarationvar hoisting and re-declaration inside loops or after use
r2-p003fix okAlternatives with delimiter spaces changed by fixModifies patterns using spaces as delimiters in alternatives, e.g., '/foo | bar/' becomes '/foo | bar/', changing token boundaries.
r2-p004fix okBlock-scoped function declaration uses altered regexfunction declarations inside blocks, switch cases, labeled statements
r2-p005fix ok prediction offASI hazard: regex on next line interpreted as divisionASI and semicolon-less code, leading ( or [ on the next line
r2-p006fix okException thrown during regex literal creationexceptions thrown inside the code the fix removes or rewrites
r2-p007fix okTab-separated value parser broken by space collapseDisrupts tab-separated value parsers where literal spaces are significant, e.g., '/\t \t/' becomes '/\t \t/'.
r2-p008fix okUnicode flag and lastIndex altered by fixregular expressions: escapes, unicode flag, lastIndex with the g flag

Sources

r1-p001 · Null/Undefined Distinction
1const input = 'a bc';
2let result;
3if ((/(a) (bc)/).test(input)) {
4 result = RegExp.$1 ?? 'no';
5} else {
6 result = undefined;
7}
8console.log(result);
--- orig.js	2026-10-17 16:03:56.217465690 -0400
+++ fixed.js	2026-10-17 16:03:56.506446164 -0400
@@ -1,6 +1,6 @@
 const input = 'a  bc';
 let result;
-if ((/(a)  (bc)/).test(input)) {
+if ((/(a) {2}(bc)/).test(input)) {
   result = RegExp.$1 ?? 'no';
 } else {
   result = undefined;
a
r1-p002 · Order of Evaluation Merged/Duplicated
1let calls = 0;
2function getStr() { return `str${++calls}`; }
3const str = getStr();
4const re = /foo ${getStr()}bar/;
5console.log(calls, re.test(str));
--- orig.js	2026-10-17 16:03:56.669435151 -0400
+++ fixed.js	2026-10-17 16:03:56.955415828 -0400
@@ -1,5 +1,5 @@
 let calls = 0;
 function getStr() { return `str${++calls}`; }
 const str = getStr();
-const re = /foo  ${getStr()}bar/;
+const re = /foo {2}${getStr()}bar/;
 console.log(calls, re.test(str));
1 false
r1-p003 · Comments Attached to Removed Code
1/** spaced */
2const re = /a /* inner */b /* end */c/;
3console.log(re.source);
--- orig.js	2026-10-17 16:03:57.127404225 -0400
+++ fixed.js	2026-10-17 16:03:57.400385818 -0400
@@ -1,3 +1,3 @@
 /** spaced */
-const re = /a  /* inner */b  /* end */c/;
+const re = /a {2}/* inner */b {2}/* end */c/;
 console.log(re.source);
r1-p004 · Strict vs Sloppy Mode Semantics
1"use strict";
2(() => {
3 const re = /\s \s/;
4 try {
5 eval("re.test(' ')");
6 console.log('ok');
7 } catch (e) {
8 console.log(e.name);
9 }
10})();
--- orig.js	2026-10-17 16:03:57.561374963 -0400
+++ fixed.js	2026-10-17 16:03:57.843355949 -0400
@@ -1,6 +1,6 @@
 "use strict";
 (() => {
-  const re = /\s  \s/;
+  const re = /\s {2}\s/;
   try {
     eval("re.test('  ')");
     console.log('ok');
ok
r1-p005 · Free-Spacing Mode (/x Flag) (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-03-4)
1/** x-mode off */
2const re = /a b/x;
3console.log(re.test('a b'), re.source);
r1-p006 · Intentional Formatting for Readability
1const re = /^ (\d{3}) - (\d{2}) - (\d{4})$/;
2const m = '123-45-6789'.match(re);
3console.log(m ? m[1] + '-' + m[2] + '-' + m[3] : null);
--- orig.js	2026-10-17 16:03:58.060341327 -0400
+++ fixed.js	2026-10-17 16:03:58.344322218 -0400
@@ -1,3 +1,3 @@
-const re = /^  (\d{3})  -  (\d{2})  -  (\d{4})$/;
+const re = /^ {2}(\d{3}) {2}- {2}(\d{2}) {2}- {2}(\d{4})$/;
 const m = '123-45-6789'.match(re);
 console.log(m ? m[1] + '-' + m[2] + '-' + m[3] : null);
null
r1-p007 · Arguments Object and Function Length
1function fn(a, b = /x y/.test('x y')) {
2 console.log(arguments.length, b);
3}
4fn(1);
--- orig.js	2026-10-17 16:03:58.516310645 -0400
+++ fixed.js	2026-10-17 16:03:58.774293285 -0400
@@ -1,4 +1,4 @@
-function fn(a, b = /x  y/.test('x  y')) {
+function fn(a, b = /x {2}y/.test('x  y')) {
   console.log(arguments.length, b);
 }
 fn(1);
1 true
r1-p008 · Source Inspection or Debugging Interference
1const re = /a b c/;
2console.log(re.source.split(' ').length - 1);
--- orig.js	2026-10-17 16:03:58.947281645 -0400
+++ fixed.js	2026-10-17 16:03:59.213263777 -0400
@@ -1,2 +1,2 @@
-const re = /a  b  c/;
+const re = /a {2}b {2}c/;
 console.log(re.source.split(' ').length - 1);
4
r2-p001 · Getter/setter accessing modified regex source
1const obj = {};
2Object.defineProperty(obj, 'pattern', {
3 get() { return /hello world/.source; },
4 enumerable: true,
5 configurable: true
6});
7console.log('Match:', /hello world/.test(obj.pattern));
--- orig.js	2026-10-17 16:04:03.154000070 -0400
+++ fixed.js	2026-10-17 16:04:03.418982422 -0400
@@ -1,7 +1,7 @@
 const obj = {};
 Object.defineProperty(obj, 'pattern', {
-  get() { return /hello  world/.source; },
+  get() { return /hello {2}world/.source; },
   enumerable: true,
   configurable: true
 });
-console.log('Match:', /hello  world/.test(obj.pattern));
+console.log('Match:', /hello {2}world/.test(obj.pattern));
Match: true
r2-p002 · Var hoisting captures altered regex before redeclaration
1function test() {
2 if (false) var r = /a b/;
3 else var r = /c d/;
4 console.log(r.test('a b'));
5}
6test();
--- orig.js	2026-10-17 16:04:03.594970702 -0400
+++ fixed.js	2026-10-17 16:04:03.869952388 -0400
@@ -1,6 +1,6 @@
 function test() {
-  if (false) var r = /a  b/;
-  else var r = /c  d/;
+  if (false) var r = /a {2}b/;
+  else var r = /c {2}d/;
   console.log(r.test('a  b'));
 }
 test();
false
r2-p003 · Alternatives with delimiter spaces changed by fix
1const re = /foo | bar/;
2console.log(re.test('foo'));
3console.log(re.test(' bar'));
4console.log(re.test('foobar'));
--- orig.js	2026-10-17 16:04:04.042940874 -0400
+++ fixed.js	2026-10-17 16:04:04.305923395 -0400
@@ -1,4 +1,4 @@
-const re = /foo  |  bar/;
+const re = /foo {2}| {2}bar/;
 console.log(re.test('foo'));
 console.log(re.test('  bar'));
 console.log(re.test('foobar'));
false
true
false
r2-p004 · Block-scoped function declaration uses altered regex
1{
2 function check(s) { return /test case/.test(s); }
3 console.log(check('test case'));
4 console.log(check('testcase'));
5}
--- orig.js	2026-10-17 16:04:04.481911699 -0400
+++ fixed.js	2026-10-17 16:04:04.752893690 -0400
@@ -1,5 +1,5 @@
 {
-  function check(s) { return /test  case/.test(s); }
+  function check(s) { return /test {2}case/.test(s); }
   console.log(check('test  case'));
   console.log(check('testcase'));
 }
true
false
r2-p005 · ASI hazard: regex on next line interpreted as division
1let x = 1
2(/space /).test('space ') ? console.log('matched') : console.log('not')
--- orig.js	2026-10-17 16:04:04.928881994 -0400
+++ fixed.js	2026-10-17 16:04:05.208863414 -0400
@@ -1,2 +1,2 @@
 let x = 1
-(/space  /).test('space  ') ? console.log('matched') : console.log('not')
+(/space {2}/).test('space  ') ? console.log('matched') : console.log('not')
r2-p006 · Exception thrown during regex literal creation
1try {
2 const re = /fail here/;
3 throw new Error('deliberate');
4} catch (e) {
5 console.log('Caught:', e.constructor.name, e.message);
6}
--- orig.js	2026-10-17 16:04:05.371852604 -0400
+++ fixed.js	2026-10-17 16:04:05.641834698 -0400
@@ -1,5 +1,5 @@
 try {
-  const re = /fail  here/;
+  const re = /fail {2}here/;
   throw new Error('deliberate');
 } catch (e) {
   console.log('Caught:', e.constructor.name, e.message);
Caught: Error deliberate
r2-p007 · Tab-separated value parser broken by space collapse
1const line = 'a\tb\tc';
2const re = /\t \t/;
3console.log('TSV match:', re.test(line));
4console.log('Expected fields:', line.split('\t'));
--- orig.js	2026-10-17 16:04:05.805823822 -0400
+++ fixed.js	2026-10-17 16:04:06.068806389 -0400
@@ -1,4 +1,4 @@
 const line = 'a\tb\tc';
-const re = /\t  \t/;
+const re = /\t {2}\t/;
 console.log('TSV match:', re.test(line));
 console.log('Expected fields:', line.split('\t'));
TSV match: false
Expected fields: [ 'a', 'b', 'c' ]
r2-p008 · Unicode flag and lastIndex altered by fix
1const re = /💩 💩/gu;
2console.log('First:', re.test('💩 💩'));
3console.log('lastIndex after:', re.lastIndex);
4re.lastIndex = 0;
5console.log('Second:', re.test('💩💩'));
--- orig.js	2026-10-17 16:04:06.229795734 -0400
+++ fixed.js	2026-10-17 16:04:06.504777534 -0400
@@ -1,4 +1,4 @@
-const re = /💩  💩/gu;
+const re = /💩 {2}💩/gu;
 console.log('First:', re.test('💩  💩'));
 console.log('lastIndex after:', re.lastIndex);
 re.lastIndex = 0;
First: true
lastIndex after: 6
Second: false